Merge "fix issue in negative line spacing with key arrow down" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / markup-processor.cpp
1 /*
2  * Copyright (c) 2022 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-anchor.h>
29 #include <dali-toolkit/internal/text/markup-processor-background.h>
30 #include <dali-toolkit/internal/text/markup-processor-character-spacing.h>
31 #include <dali-toolkit/internal/text/markup-processor-color.h>
32 #include <dali-toolkit/internal/text/markup-processor-embedded-item.h>
33 #include <dali-toolkit/internal/text/markup-processor-font.h>
34 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
35 #include <dali-toolkit/internal/text/markup-processor-paragraph.h>
36 #include <dali-toolkit/internal/text/markup-processor-span.h>
37 #include <dali-toolkit/internal/text/markup-processor-strikethrough.h>
38 #include <dali-toolkit/internal/text/markup-processor-underline.h>
39 #include <dali-toolkit/internal/text/xhtml-entities.h>
40
41 namespace Dali
42 {
43 namespace Toolkit
44 {
45 namespace Text
46 {
47 namespace
48 {
49 // HTML-ISH tag and attribute constants.
50 // Note they must be lower case in order to make the comparison to work
51 // as the parser converts all the read tags to lower case.
52 const std::string XHTML_COLOR_TAG("color");
53 const std::string XHTML_FONT_TAG("font");
54 const std::string XHTML_B_TAG("b");
55 const std::string XHTML_I_TAG("i");
56 const std::string XHTML_U_TAG("u");
57 const std::string XHTML_SHADOW_TAG("shadow");
58 const std::string XHTML_GLOW_TAG("glow");
59 const std::string XHTML_OUTLINE_TAG("outline");
60 const std::string XHTML_ITEM_TAG("item");
61 const std::string XHTML_ANCHOR_TAG("a");
62 const std::string XHTML_BACKGROUND_TAG("background");
63 const std::string XHTML_SPAN_TAG("span");
64 const std::string XHTML_STRIKETHROUGH_TAG("s");
65 const std::string XHTML_PARAGRAPH_TAG("p");
66 const std::string XHTML_CHARACTER_SPACING_TAG("char-spacing");
67
68 const char LESS_THAN      = '<';
69 const char GREATER_THAN   = '>';
70 const char EQUAL          = '=';
71 const char QUOTATION_MARK = '\'';
72 const char SLASH          = '/';
73 const char BACK_SLASH     = '\\';
74 const char AMPERSAND      = '&';
75 const char HASH           = '#';
76 const char SEMI_COLON     = ';';
77 const char CHAR_ARRAY_END = '\0';
78 const char HEX_CODE       = 'x';
79
80 const char WHITE_SPACE = 0x20; // ASCII value of the white space.
81 const char NEW_LINE    = 0x0A; // ASCII value of the newline.
82
83 // Range 1 0x0u < XHTML_DECIMAL_ENTITY_RANGE <= 0xD7FFu
84 // Range 2 0xE000u < XHTML_DECIMAL_ENTITY_RANGE <= 0xFFFDu
85 // Range 3 0x10000u < XHTML_DECIMAL_ENTITY_RANGE <= 0x10FFFFu
86 const unsigned long XHTML_DECIMAL_ENTITY_RANGE[] = {0x0u, 0xD7FFu, 0xE000u, 0xFFFDu, 0x10000u, 0x10FFFFu};
87
88 // The MAX_NUM_OF_ATTRIBUTES is the number of attributes in span tag "markup-processor-span.cpp". Because it contains the maximum number of attributes in  all tags.
89 const unsigned int MAX_NUM_OF_ATTRIBUTES = 14u; ///< The span tag has the 'font-family', 'font-size' 'font-weight', 'font-width', 'font-slant','text-color', 'u-color', 'u-height','u-type','u-dash-gap', 'u-dash-width', 's-color', 's-height' and 'char-space-value' attrubutes.
90 const unsigned int DEFAULT_VECTOR_SIZE   = 16u; ///< Default size of run vectors.
91
92 #if defined(DEBUG_ENABLED)
93 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MARKUP_PROCESSOR");
94 #endif
95
96 typedef VectorBase::SizeType RunIndex;
97
98 /**
99  * @brief Struct used to retrieve the style runs from the mark-up string.
100  */
101 template<typename StyleStackType>
102 struct StyleStack
103 {
104   Vector<StyleStackType> stack;    ///< Use a vector as a style stack.
105   unsigned int           topIndex; ///< Points the top of the stack.
106
107   StyleStack()
108   : stack(),
109     topIndex(0u)
110   {
111     stack.Resize(DEFAULT_VECTOR_SIZE);
112   }
113
114   void Push(StyleStackType item)
115   {
116     // Check if there is space inside the style stack.
117     const VectorBase::SizeType size = stack.Count();
118     if(topIndex >= size)
119     {
120       // Resize the style stack.
121       stack.Resize(2u * size);
122     }
123
124     // Set the item in the top of the stack.
125     *(stack.Begin() + topIndex) = item;
126
127     // Reposition the pointer to the top of the stack.
128     ++topIndex;
129   }
130
131   StyleStackType Pop()
132   {
133     // Pop the top of the stack.
134     --topIndex;
135     return *(stack.Begin() + topIndex);
136   }
137 };
138
139 /**
140  * @brief Struct used to retrieve spans from the mark-up string.
141  */
142 struct Span
143 {
144   RunIndex colorRunIndex;
145   RunIndex fontRunIndex;
146   RunIndex underlinedCharacterRunIndex;
147   RunIndex backgroundColorRunIndex;
148   RunIndex strikethroughCharacterRunIndex;
149   RunIndex characterSpacingCharacterRunIndex;
150
151   bool isColorDefined;
152   bool isFontDefined;
153   bool isUnderlinedCharacterDefined;
154   bool isBackgroundColorDefined;
155   bool isStrikethroughDefined;
156   bool isCharacterSpacingDefined;
157 };
158
159 /**
160  * @brief Initializes a font run description to its defaults.
161  *
162  * @param[in,out] fontRun The font description run to initialize.
163  */
164 void Initialize(FontDescriptionRun& fontRun)
165 {
166   fontRun.characterRun.characterIndex     = 0u;
167   fontRun.characterRun.numberOfCharacters = 0u;
168   fontRun.familyName                      = NULL;
169   fontRun.familyLength                    = 0u;
170   fontRun.weight                          = TextAbstraction::FontWeight::NORMAL;
171   fontRun.width                           = TextAbstraction::FontWidth::NORMAL;
172   fontRun.slant                           = TextAbstraction::FontSlant::NORMAL;
173   fontRun.size                            = 0u;
174   fontRun.familyDefined                   = false;
175   fontRun.weightDefined                   = false;
176   fontRun.widthDefined                    = false;
177   fontRun.slantDefined                    = false;
178   fontRun.sizeDefined                     = false;
179 }
180
181 /**
182  * @brief Initializes a color run description to its defaults.
183  *
184  * @param[in,out] colorRun The font description run to initialize.
185  */
186 void Initialize(ColorRun& colorRun)
187 {
188   colorRun.characterRun.characterIndex     = 0u;
189   colorRun.characterRun.numberOfCharacters = 0u;
190 }
191
192 /**
193  * @brief Initializes a underlined character run to its defaults.
194  *
195  * @param[in,out] underlinedCharacterRun The underelined character run to initialize.
196  */
197 void Initialize(UnderlinedCharacterRun& underlinedCharacterRun)
198 {
199   underlinedCharacterRun.characterRun.characterIndex     = 0u;
200   underlinedCharacterRun.characterRun.numberOfCharacters = 0u;
201 }
202
203 /**
204  * @brief Initializes a span to its defaults.
205  *
206  * @param[in,out] span The span to be initialized.
207  */
208 void Initialize(Span& span)
209 {
210   span.colorRunIndex  = 0u;
211   span.isColorDefined = false;
212
213   span.fontRunIndex  = 0u;
214   span.isFontDefined = false;
215
216   span.underlinedCharacterRunIndex  = 0u;
217   span.isUnderlinedCharacterDefined = false;
218   span.backgroundColorRunIndex      = 0u;
219   span.isBackgroundColorDefined     = false;
220
221   //strikethrough
222   span.strikethroughCharacterRunIndex = 0u;
223   span.isStrikethroughDefined         = false;
224
225   //characterSpacing
226   span.characterSpacingCharacterRunIndex = 0u;
227   span.isCharacterSpacingDefined         = false;
228 }
229
230 /**
231  * @brief Initializes a strikethrough character run to its defaults.
232  *
233  * @param[in,out] strikethroughCharacterRun The strikethrough character run to initialize.
234  */
235 void Initialize(StrikethroughCharacterRun& strikethroughCharacterRun)
236 {
237   strikethroughCharacterRun.characterRun.characterIndex     = 0u;
238   strikethroughCharacterRun.characterRun.numberOfCharacters = 0u;
239   strikethroughCharacterRun.properties.colorDefined         = false;
240 }
241
242 /**
243  * @brief Initializes a  bounded-paragraph character run to its defaults.
244  *
245  * @param[in,out] boundedParagraphRun The bounded paragraphRun run to initialize.
246  */
247 void Initialize(BoundedParagraphRun& boundedParagraphRun)
248 {
249   boundedParagraphRun.characterRun.characterIndex     = 0u;
250   boundedParagraphRun.characterRun.numberOfCharacters = 0u;
251 }
252
253 /**
254  * @brief Initializes a character-spacing run to its defaults.
255  *
256  * @param[in,out] characterSpacingCharacterRun The character-spacing run to initialize.
257  */
258 void Initialize(CharacterSpacingCharacterRun& characterSpacingCharacterRun)
259 {
260   characterSpacingCharacterRun.characterRun.characterIndex     = 0u;
261   characterSpacingCharacterRun.characterRun.numberOfCharacters = 0u;
262   characterSpacingCharacterRun.value                           = 0.0f;
263 }
264
265 /**
266  * @brief Splits the tag string into the tag name and its attributes.
267  *
268  * The attributes are stored in a vector in the tag.
269  *
270  * @param[in,out] tag The tag.
271  */
272 void ParseAttributes(Tag& tag)
273 {
274   if(tag.buffer == NULL)
275   {
276     return;
277   }
278
279   tag.attributes.Resize(MAX_NUM_OF_ATTRIBUTES);
280
281   // Find first the tag name.
282   bool isQuotationOpen = false;
283
284   const char*       tagBuffer    = tag.buffer;
285   const char* const tagEndBuffer = tagBuffer + tag.length;
286   tag.length                     = 0u;
287   for(; tagBuffer < tagEndBuffer; ++tagBuffer)
288   {
289     const char character = *tagBuffer;
290     if(WHITE_SPACE < character)
291     {
292       ++tag.length;
293     }
294     else
295     {
296       // Stops counting the length of the tag when a white space is found.
297       // @note a white space is the WHITE_SPACE character and anything below as 'tab', 'return' or 'control characters'.
298       break;
299     }
300   }
301   SkipWhiteSpace(tagBuffer, tagEndBuffer);
302
303   // Find the attributes.
304   unsigned int attributeIndex = 0u;
305   const char*  nameBuffer     = NULL;
306   const char*  valueBuffer    = NULL;
307   Length       nameLength     = 0u;
308   Length       valueLength    = 0u;
309
310   bool   addToNameValue     = true;
311   Length numberOfWhiteSpace = 0u;
312   for(; tagBuffer < tagEndBuffer; ++tagBuffer)
313   {
314     const char character = *tagBuffer;
315     if((WHITE_SPACE >= character) && !isQuotationOpen)
316     {
317       if(NULL != valueBuffer)
318       {
319         // Remove white spaces at the end of the value.
320         valueLength -= numberOfWhiteSpace;
321       }
322
323       if((NULL != nameBuffer) && (NULL != valueBuffer))
324       {
325         // Every time a white space is found, a new attribute is created and stored in the attributes vector.
326         Attribute& attribute = *(tag.attributes.Begin() + attributeIndex);
327         ++attributeIndex;
328
329         attribute.nameBuffer  = nameBuffer;
330         attribute.valueBuffer = valueBuffer;
331         attribute.nameLength  = nameLength;
332         attribute.valueLength = valueLength;
333
334         nameBuffer  = NULL;
335         valueBuffer = NULL;
336         nameLength  = 0u;
337         valueLength = 0u;
338
339         addToNameValue = true; // next read characters will be added to the name.
340       }
341     }
342     else if(EQUAL == character) // '='
343     {
344       addToNameValue = false; // next read characters will be added to the value.
345       SkipWhiteSpace(tagBuffer, tagEndBuffer);
346     }
347     else if(QUOTATION_MARK == character) // '\''
348     {
349       // Do not add quotation marks to neither name nor value.
350       isQuotationOpen = !isQuotationOpen;
351
352       if(isQuotationOpen)
353       {
354         ++tagBuffer;
355         SkipWhiteSpace(tagBuffer, tagEndBuffer);
356         --tagBuffer;
357       }
358     }
359     else
360     {
361       // Adds characters to the name or the value.
362       if(addToNameValue)
363       {
364         if(NULL == nameBuffer)
365         {
366           nameBuffer = tagBuffer;
367         }
368         ++nameLength;
369       }
370       else
371       {
372         if(isQuotationOpen)
373         {
374           if(WHITE_SPACE >= character)
375           {
376             ++numberOfWhiteSpace;
377           }
378           else
379           {
380             numberOfWhiteSpace = 0u;
381           }
382         }
383         if(NULL == valueBuffer)
384         {
385           valueBuffer = tagBuffer;
386         }
387         ++valueLength;
388       }
389     }
390   }
391
392   if(NULL != valueBuffer)
393   {
394     // Remove white spaces at the end of the value.
395     valueLength -= numberOfWhiteSpace;
396   }
397
398   if((NULL != nameBuffer) && (NULL != valueBuffer))
399   {
400     // Checks if the last attribute needs to be added.
401     Attribute& attribute = *(tag.attributes.Begin() + attributeIndex);
402     ++attributeIndex;
403
404     attribute.nameBuffer  = nameBuffer;
405     attribute.valueBuffer = valueBuffer;
406     attribute.nameLength  = nameLength;
407     attribute.valueLength = valueLength;
408   }
409
410   // Resize the vector of attributes.
411   tag.attributes.Resize(attributeIndex);
412 }
413
414 /**
415  * @brief It parses a tag and its attributes if the given iterator @e it is pointing at a tag beginning.
416  *
417  * @param[in,out] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
418  * @param[in] markupStringEndBuffer Pointer to one character after the end of the mark-up string buffer.
419  * @param[out] tag The tag with its attributes.
420  *
421  * @return @e true if the iterator @e it is pointing a mark-up tag. Otherwise @e false.
422  */
423 bool IsTag(const char*&      markupStringBuffer,
424            const char* const markupStringEndBuffer,
425            Tag&              tag)
426 {
427   bool isTag              = false;
428   bool isQuotationOpen    = false;
429   bool attributesFound    = false;
430   tag.isEndTag            = false;
431   bool isPreviousLessThan = false;
432   bool isPreviousSlash    = false;
433
434   const char character = *markupStringBuffer;
435   if(LESS_THAN == character) // '<'
436   {
437     tag.buffer         = NULL;
438     tag.length         = 0u;
439     isPreviousLessThan = true;
440
441     // if the iterator is pointing to a '<' character, then check if it's a mark-up tag is needed.
442     ++markupStringBuffer;
443     if(markupStringBuffer < markupStringEndBuffer)
444     {
445       SkipWhiteSpace(markupStringBuffer, markupStringEndBuffer);
446
447       for(; (!isTag) && (markupStringBuffer < markupStringEndBuffer); ++markupStringBuffer)
448       {
449         const char character = *markupStringBuffer;
450
451         if(!isQuotationOpen && (SLASH == character)) // '/'
452         {
453           if(isPreviousLessThan)
454           {
455             tag.isEndTag = true;
456           }
457           else
458           {
459             // if the tag has a '/' it may be an end tag.
460             isPreviousSlash = true;
461           }
462
463           isPreviousLessThan = false;
464           if((markupStringBuffer + 1u < markupStringEndBuffer) && (WHITE_SPACE >= *(markupStringBuffer + 1u)))
465           {
466             ++markupStringBuffer;
467             SkipWhiteSpace(markupStringBuffer, markupStringEndBuffer);
468             --markupStringBuffer;
469           }
470         }
471         else if(GREATER_THAN == character) // '>'
472         {
473           isTag = true;
474           if(isPreviousSlash)
475           {
476             tag.isEndTag = true;
477           }
478
479           isPreviousSlash    = false;
480           isPreviousLessThan = false;
481         }
482         else if(QUOTATION_MARK == character)
483         {
484           isQuotationOpen = !isQuotationOpen;
485           ++tag.length;
486
487           isPreviousSlash    = false;
488           isPreviousLessThan = false;
489         }
490         else if(WHITE_SPACE >= character) // ' '
491         {
492           // If the tag contains white spaces then it may have attributes.
493           if(!isQuotationOpen)
494           {
495             attributesFound = true;
496           }
497           ++tag.length;
498         }
499         else
500         {
501           if(NULL == tag.buffer)
502           {
503             tag.buffer = markupStringBuffer;
504           }
505
506           // If it's not any of the 'special' characters then just add it to the tag string.
507           ++tag.length;
508
509           isPreviousSlash    = false;
510           isPreviousLessThan = false;
511         }
512       }
513     }
514
515     // If the tag string has white spaces, then parse the attributes is needed.
516     if(attributesFound)
517     {
518       ParseAttributes(tag);
519     }
520   }
521
522   return isTag;
523 }
524
525 /**
526  * @brief Returns length of XHTML entity by parsing the text. It also determines if it is XHTML entity or not.
527  *
528  * @param[in] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
529  * @param[in] markupStringEndBuffer Pointing to end of mark-up string buffer.
530  *
531  * @return Length of markupText in case of XHTML entity otherwise return 0.
532  */
533 unsigned int GetXHTMLEntityLength(const char*&      markupStringBuffer,
534                                   const char* const markupStringEndBuffer)
535 {
536   char character = *markupStringBuffer;
537   if(AMPERSAND == character) // '&'
538   {
539     // if the iterator is pointing to a '&' character, then check for ';' to find end to XHTML entity.
540     ++markupStringBuffer;
541     if(markupStringBuffer < markupStringEndBuffer)
542     {
543       unsigned int len = 1u;
544       for(; markupStringBuffer < markupStringEndBuffer; ++markupStringBuffer)
545       {
546         character = *markupStringBuffer;
547         ++len;
548         if(SEMI_COLON == character) // ';'
549         {
550           // found end of XHTML entity
551           ++markupStringBuffer;
552           return len;
553         }
554         else if((AMPERSAND == character) || (BACK_SLASH == character) || (LESS_THAN == character))
555         {
556           return 0;
557         }
558       }
559     }
560   }
561   return 0;
562 }
563
564 /**
565  * @brief It parses a XHTML string which has hex/decimal entity and fill its corresponging utf-8 string.
566  *
567  * @param[in] markupText The mark-up text buffer.
568  * @param[out] utf-8 text Corresponding to markup Text
569  *
570  * @return true if string is successfully parsed otherwise false
571  */
572 bool XHTMLNumericEntityToUtf8(const char* markupText, char* utf8)
573 {
574   bool result = false;
575
576   if(NULL != markupText)
577   {
578     bool isHex = false;
579
580     // check if hex or decimal entity
581     if((CHAR_ARRAY_END != *markupText) && (HEX_CODE == *markupText))
582     {
583       isHex = true;
584       ++markupText;
585     }
586
587     char*         end = NULL;
588     unsigned long l   = strtoul(markupText, &end, (isHex ? 16 : 10)); // l contains UTF-32 code in case of correct XHTML entity
589
590     // check for valid XHTML numeric entities (between '#' or "#x" and ';')
591     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'
592     {
593       /* characters XML 1.1 permits */
594       if(((XHTML_DECIMAL_ENTITY_RANGE[0] < l) && (l <= XHTML_DECIMAL_ENTITY_RANGE[1])) ||
595          ((XHTML_DECIMAL_ENTITY_RANGE[2] <= l) && (l <= XHTML_DECIMAL_ENTITY_RANGE[3])) ||
596          ((XHTML_DECIMAL_ENTITY_RANGE[4] <= l) && (l <= XHTML_DECIMAL_ENTITY_RANGE[5])))
597       {
598         // Convert UTF32 code to UTF8
599         Utf32ToUtf8(reinterpret_cast<const uint32_t* const>(&l), 1, reinterpret_cast<uint8_t*>(utf8));
600         result = true;
601       }
602     }
603   }
604   return result;
605 }
606
607 /**
608  * @brief Processes a particular tag for the required run (color-run, font-run or underlined-character-run).
609  *
610  * @tparam RunType Whether ColorRun , FontDescriptionRun or UnderlinedCharacterRun
611  *
612  * @param[in/out] runsContainer The container containing all the runs
613  * @param[in/out] styleStack The style stack
614  * @param[in] tag The tag we are currently processing
615  * @param[in] characterIndex The current character index
616  * @param[in/out] runIndex The run index
617  * @param[in/out] tagReference The tagReference we should increment/decrement
618  * @param[in] parameterSettingFunction This function will be called to set run specific parameters
619  */
620 template<typename RunType>
621 void ProcessTagForRun(
622   Vector<RunType>&                          runsContainer,
623   StyleStack<RunIndex>&                     styleStack,
624   const Tag&                                tag,
625   const CharacterIndex                      characterIndex,
626   RunIndex&                                 runIndex,
627   int&                                      tagReference,
628   std::function<void(const Tag&, RunType&)> parameterSettingFunction)
629 {
630   if(!tag.isEndTag)
631   {
632     // Create a new run.
633     RunType run;
634     Initialize(run);
635
636     // Fill the run with the parameters.
637     run.characterRun.characterIndex = characterIndex;
638     parameterSettingFunction(tag, run);
639
640     // Push the run in the logical model.
641     runsContainer.PushBack(run);
642
643     // Push the index of the run into the stack.
644     styleStack.Push(runIndex);
645
646     // Point the next free run.
647     ++runIndex;
648
649     // Increase reference
650     ++tagReference;
651   }
652   else
653   {
654     if(tagReference > 0)
655     {
656       // Pop the top of the stack and set the number of characters of the run.
657       RunType& run                        = *(runsContainer.Begin() + styleStack.Pop());
658       run.characterRun.numberOfCharacters = characterIndex - run.characterRun.characterIndex;
659       --tagReference;
660     }
661   }
662 }
663
664 /**
665  * @brief Processes the item tag
666  *
667  * @param[in/out] markupProcessData The markup process data
668  * @param[in] tag The current tag
669  * @param[in/out] characterIndex The current character index
670  */
671 void ProcessItemTag(
672   MarkupProcessData& markupProcessData,
673   const Tag          tag,
674   CharacterIndex&    characterIndex)
675 {
676   if(tag.isEndTag)
677   {
678     // Create an embedded item instance.
679     EmbeddedItem item;
680     item.characterIndex = characterIndex;
681     ProcessEmbeddedItem(tag, item);
682
683     markupProcessData.items.PushBack(item);
684
685     // Insert white space character that will be replaced by the item.
686     markupProcessData.markupProcessedText.append(1u, WHITE_SPACE);
687     ++characterIndex;
688   }
689 }
690
691 /**
692  * @brief Processes the paragraph-tag
693  *
694  * @param[in/out] markupProcessData The markup process data
695  * @param[in] tag The current tag
696  * @param[in] isEndBuffer Whether the end of buffer
697  * @param[in/out] characterIndex The current character index
698  */
699 void ProcessParagraphTag(
700   MarkupProcessData& markupProcessData,
701   const Tag          tag,
702   bool               isEndBuffer,
703   CharacterIndex&    characterIndex)
704 {
705   if((characterIndex > 0 &&
706       markupProcessData.markupProcessedText[characterIndex - 1u] != NEW_LINE) &&
707      (!(tag.isEndTag && isEndBuffer)))
708   {
709     // Insert new-line character at the start and end of paragraph.
710     markupProcessData.markupProcessedText.append(1u, NEW_LINE);
711     ++characterIndex;
712   }
713 }
714
715 /**
716  * @brief Processes the anchor tag
717  *
718  * @param[in/out] markupProcessData The markup process data
719  * @param[in] tag The current tag
720  * @param[in/out] characterIndex The current character index
721  */
722 void ProcessAnchorTag(
723   MarkupProcessData& markupProcessData,
724   const Tag          tag,
725   CharacterIndex&    characterIndex)
726 {
727   if(!tag.isEndTag)
728   {
729     // Create an anchor instance.
730     Anchor anchor;
731     anchor.startIndex = characterIndex;
732     anchor.endIndex   = 0u;
733     ProcessAnchor(tag, anchor);
734     markupProcessData.anchors.PushBack(anchor);
735   }
736   else
737   {
738     // Update end index.
739     unsigned int count = markupProcessData.anchors.Count();
740     if(count > 0)
741     {
742       markupProcessData.anchors[count - 1].endIndex = characterIndex;
743     }
744   }
745 }
746
747 /**
748  * @brief Processes span tag for the color-run & font-run.
749  *
750  * @param[in] spanTag The tag we are currently processing
751  * @param[inout] spanStack The spans stack
752  * @param[inout] colorRuns The container containing all the color runs
753  * @param[inout] fontRuns The container containing all the font description runs
754  * @param[inout] underlinedCharacterRuns The container containing all the underlined character runs
755  * @param[inout] strikethroughCharacterRuns The container containing all the strikethroughed character runs
756  * @param[inout] colorRunIndex The color run index
757  * @param[inout] fontRunIndex The font run index
758  * @param[inout] underlinedCharacterRunIndex The underlined character run index
759  * @param[inout] strikethroughCharacterRunIndex The strikethroughed character run index
760  * @param[in] characterIndex The current character index
761  * @param[in] tagReference The tagReference we should increment/decrement
762  */
763 void ProcessSpanForRun(
764   const Tag&                            spanTag,
765   StyleStack<Span>&                     spanStack,
766   Vector<ColorRun>&                     colorRuns,
767   Vector<FontDescriptionRun>&           fontRuns,
768   Vector<UnderlinedCharacterRun>&       underlinedCharacterRuns,
769   Vector<ColorRun>&                     backgroundColorRuns,
770   Vector<StrikethroughCharacterRun>&    strikethroughCharacterRuns,
771   Vector<CharacterSpacingCharacterRun>& characterSpacingCharacterRuns,
772   RunIndex&                             colorRunIndex,
773   RunIndex&                             fontRunIndex,
774   RunIndex&                             underlinedCharacterRunIndex,
775   RunIndex&                             backgroundColorRunIndex,
776   RunIndex&                             strikethroughCharacterRunIndex,
777   RunIndex&                             characterSpacingCharacterRunIndex,
778   const CharacterIndex                  characterIndex,
779   int&                                  tagReference)
780 {
781   if(!spanTag.isEndTag)
782   {
783     // Create a new run.
784     ColorRun colorRun;
785     Initialize(colorRun);
786
787     FontDescriptionRun fontRun;
788     Initialize(fontRun);
789
790     UnderlinedCharacterRun underlinedCharacterRun;
791     Initialize(underlinedCharacterRun);
792
793     ColorRun backgroundColorRun;
794     Initialize(backgroundColorRun);
795
796     StrikethroughCharacterRun strikethroughCharacterRun;
797     Initialize(strikethroughCharacterRun);
798
799     CharacterSpacingCharacterRun characterSpacingCharacterRun;
800     Initialize(characterSpacingCharacterRun);
801
802     Span span;
803     Initialize(span);
804
805     // Fill the run with the parameters.
806     colorRun.characterRun.characterIndex                     = characterIndex;
807     fontRun.characterRun.characterIndex                      = characterIndex;
808     underlinedCharacterRun.characterRun.characterIndex       = characterIndex;
809     backgroundColorRun.characterRun.characterIndex           = characterIndex;
810     strikethroughCharacterRun.characterRun.characterIndex    = characterIndex;
811     characterSpacingCharacterRun.characterRun.characterIndex = characterIndex;
812
813     span.colorRunIndex                     = colorRunIndex;
814     span.fontRunIndex                      = fontRunIndex;
815     span.underlinedCharacterRunIndex       = underlinedCharacterRunIndex;
816     span.backgroundColorRunIndex           = backgroundColorRunIndex;
817     span.strikethroughCharacterRunIndex    = strikethroughCharacterRunIndex;
818     span.characterSpacingCharacterRunIndex = characterSpacingCharacterRunIndex;
819
820     ProcessSpanTag(spanTag,
821                    colorRun,
822                    fontRun,
823                    underlinedCharacterRun,
824                    backgroundColorRun,
825                    strikethroughCharacterRun,
826                    characterSpacingCharacterRun,
827                    span.isColorDefined,
828                    span.isFontDefined,
829                    span.isUnderlinedCharacterDefined,
830                    span.isBackgroundColorDefined,
831                    span.isStrikethroughDefined,
832                    span.isCharacterSpacingDefined);
833
834     // Push the span into the stack.
835     spanStack.Push(span);
836
837     // Point the next free run.
838     if(span.isColorDefined)
839     {
840       // Push the run in the logical model.
841       colorRuns.PushBack(colorRun);
842       ++colorRunIndex;
843     }
844
845     if(span.isFontDefined)
846     {
847       // Push the run in the logical model.
848       fontRuns.PushBack(fontRun);
849       ++fontRunIndex;
850     }
851
852     if(span.isUnderlinedCharacterDefined)
853     {
854       // Push the run in the logical model.
855       underlinedCharacterRuns.PushBack(underlinedCharacterRun);
856       ++underlinedCharacterRunIndex;
857     }
858
859     if(span.isBackgroundColorDefined)
860     {
861       // Push the run in the logical model.
862       backgroundColorRuns.PushBack(backgroundColorRun);
863       ++backgroundColorRunIndex;
864     }
865
866     if(span.isStrikethroughDefined)
867     {
868       // Push the run in the logical model.
869       strikethroughCharacterRuns.PushBack(strikethroughCharacterRun);
870       ++strikethroughCharacterRunIndex;
871     }
872
873     if(span.isCharacterSpacingDefined)
874     {
875       // Push the run in the logical model.
876       characterSpacingCharacterRuns.PushBack(characterSpacingCharacterRun);
877       ++characterSpacingCharacterRunIndex;
878     }
879
880     // Increase reference
881     ++tagReference;
882   }
883   else
884   {
885     if(tagReference > 0)
886     {
887       // Pop the top of the stack and set the number of characters of the run.
888       Span span = spanStack.Pop();
889
890       if(span.isColorDefined)
891       {
892         ColorRun& colorRun                       = *(colorRuns.Begin() + span.colorRunIndex);
893         colorRun.characterRun.numberOfCharacters = characterIndex - colorRun.characterRun.characterIndex;
894       }
895
896       if(span.isFontDefined)
897       {
898         FontDescriptionRun& fontRun             = *(fontRuns.Begin() + span.fontRunIndex);
899         fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
900       }
901
902       if(span.isUnderlinedCharacterDefined)
903       {
904         UnderlinedCharacterRun& underlinedCharacterRun         = *(underlinedCharacterRuns.Begin() + span.underlinedCharacterRunIndex);
905         underlinedCharacterRun.characterRun.numberOfCharacters = characterIndex - underlinedCharacterRun.characterRun.characterIndex;
906       }
907
908       if(span.isBackgroundColorDefined)
909       {
910         ColorRun& backgroundColorRun                       = *(backgroundColorRuns.Begin() + span.backgroundColorRunIndex);
911         backgroundColorRun.characterRun.numberOfCharacters = characterIndex - backgroundColorRun.characterRun.characterIndex;
912       }
913
914       if(span.isStrikethroughDefined)
915       {
916         StrikethroughCharacterRun& strikethroughCharacterRun      = *(strikethroughCharacterRuns.Begin() + span.strikethroughCharacterRunIndex);
917         strikethroughCharacterRun.characterRun.numberOfCharacters = characterIndex - strikethroughCharacterRun.characterRun.characterIndex;
918       }
919
920       if(span.isCharacterSpacingDefined)
921       {
922         CharacterSpacingCharacterRun& characterSpacingCharacterRun   = *(characterSpacingCharacterRuns.Begin() + span.characterSpacingCharacterRunIndex);
923         characterSpacingCharacterRun.characterRun.numberOfCharacters = characterIndex - characterSpacingCharacterRun.characterRun.characterIndex;
924       }
925
926       --tagReference;
927     }
928   }
929 }
930
931 /**
932  * @brief Resizes the model's vectors
933  *
934  * @param[inout] markupProcessData The markup process data
935  * @param[in] fontRunIndex The font run index
936  * @param[in] colorRunIndex The color run index
937  * @param[in] underlinedCharacterRunIndex The underlined character run index
938  * @param[in] strikethroughCharacterRunIndex The strikethroughed character run index
939  * @param[in] backgroundRunIndex The background run index
940  * @param[in] boundedParagraphRunIndex The bounded paragraph run index
941  * @param[in] characterSpacingCharacterRunIndex The character-spacing character run index
942  *
943  */
944 void ResizeModelVectors(MarkupProcessData& markupProcessData,
945                         const RunIndex     fontRunIndex,
946                         const RunIndex     colorRunIndex,
947                         const RunIndex     underlinedCharacterRunIndex,
948                         const RunIndex     strikethroughCharacterRunIndex,
949                         const RunIndex     backgroundRunIndex,
950                         const RunIndex     boundedParagraphRunIndex,
951                         const RunIndex     characterSpacingCharacterRunIndex)
952 {
953   markupProcessData.fontRuns.Resize(fontRunIndex);
954   markupProcessData.colorRuns.Resize(colorRunIndex);
955   markupProcessData.underlinedCharacterRuns.Resize(underlinedCharacterRunIndex);
956   markupProcessData.strikethroughCharacterRuns.Resize(strikethroughCharacterRunIndex);
957   markupProcessData.backgroundColorRuns.Resize(backgroundRunIndex);
958   markupProcessData.boundedParagraphRuns.Resize(boundedParagraphRunIndex);
959   markupProcessData.characterSpacingCharacterRuns.Resize(characterSpacingCharacterRunIndex);
960
961 #ifdef DEBUG_ENABLED
962   for(unsigned int i = 0; i < colorRunIndex; ++i)
963   {
964     ColorRun& run = markupProcessData.colorRuns[i];
965     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);
966   }
967 #endif
968 }
969
970 /**
971  * @brief Processes the markup string buffer
972  *
973  * @param[in/out] markupProcessData The markup process data
974  * @param[in/out] markupStringBuffer The markup string buffer pointer
975  * @param[in] markupStringEndBuffer The markup string end buffer pointer
976  * @param[in/out] characterIndex The current character index
977  */
978 void ProcessMarkupStringBuffer(
979   MarkupProcessData& markupProcessData,
980   const char*&       markupStringBuffer,
981   const char* const  markupStringEndBuffer,
982   CharacterIndex&    characterIndex)
983 {
984   unsigned char character    = *markupStringBuffer;
985   const char*   markupBuffer = markupStringBuffer;
986   unsigned char count        = GetUtf8Length(character);
987   char          utf8[8];
988
989   if((BACK_SLASH == character) && (markupStringBuffer + 1u < markupStringEndBuffer))
990   {
991     // Adding < , >  or & special character.
992     const unsigned char nextCharacter = *(markupStringBuffer + 1u);
993     if((LESS_THAN == nextCharacter) || (GREATER_THAN == nextCharacter) || (AMPERSAND == nextCharacter))
994     {
995       character = nextCharacter;
996       ++markupStringBuffer;
997
998       count        = GetUtf8Length(character);
999       markupBuffer = markupStringBuffer;
1000     }
1001   }
1002   else // checking if contains XHTML entity or not
1003   {
1004     const unsigned int len = GetXHTMLEntityLength(markupStringBuffer, markupStringEndBuffer);
1005
1006     // Parse markupStringTxt if it contains XHTML Entity between '&' and ';'
1007     if(len > 0)
1008     {
1009       char* entityCode = NULL;
1010       bool  result     = false;
1011       count            = 0;
1012
1013       // Checking if XHTML Numeric Entity
1014       if(HASH == *(markupBuffer + 1u))
1015       {
1016         entityCode = &utf8[0];
1017         // markupBuffer is currently pointing to '&'. By adding 2u to markupBuffer it will point to numeric string by skipping "&#'
1018         result = XHTMLNumericEntityToUtf8((markupBuffer + 2u), entityCode);
1019       }
1020       else // Checking if XHTML Named Entity
1021       {
1022         entityCode = const_cast<char*>(NamedEntityToUtf8(markupBuffer, len));
1023         result     = (entityCode != NULL);
1024       }
1025       if(result)
1026       {
1027         markupBuffer = entityCode; //utf8 text assigned to markupBuffer
1028         character    = markupBuffer[0];
1029       }
1030       else
1031       {
1032         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Not valid XHTML entity : (%.*s) \n", len, markupBuffer);
1033         markupBuffer = NULL;
1034       }
1035     }
1036     else // in case string conatins Start of XHTML Entity('&') but not its end character(';')
1037     {
1038       if(character == AMPERSAND)
1039       {
1040         markupBuffer = NULL;
1041         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Not Well formed XHTML content \n");
1042       }
1043     }
1044   }
1045
1046   if(markupBuffer != NULL)
1047   {
1048     const unsigned char numberOfBytes = GetUtf8Length(character);
1049     markupProcessData.markupProcessedText.push_back(character);
1050
1051     for(unsigned char i = 1u; i < numberOfBytes; ++i)
1052     {
1053       ++markupBuffer;
1054       markupProcessData.markupProcessedText.push_back(*markupBuffer);
1055     }
1056
1057     ++characterIndex;
1058     markupStringBuffer += count;
1059   }
1060 }
1061
1062 } // namespace
1063
1064 void ProcessMarkupString(const std::string& markupString, MarkupProcessData& markupProcessData)
1065 {
1066   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "markupString: %s\n", markupString.c_str());
1067
1068   // Reserve space for the plain text.
1069   const Length markupStringSize = markupString.size();
1070   markupProcessData.markupProcessedText.reserve(markupStringSize);
1071
1072   // Stores a struct with the index to the first character of the run, the type of run and its parameters.
1073   StyleStack<RunIndex> styleStack;
1074
1075   // Stores a struct with the index to the first character of the color run & color font for the span.
1076   StyleStack<Span> spanStack;
1077
1078   // Points the next free position in the vector of runs.
1079   RunIndex colorRunIndex                     = 0u;
1080   RunIndex fontRunIndex                      = 0u;
1081   RunIndex underlinedCharacterRunIndex       = 0u;
1082   RunIndex backgroundRunIndex                = 0u;
1083   RunIndex strikethroughCharacterRunIndex    = 0u;
1084   RunIndex boundedParagraphRunIndex          = 0u;
1085   RunIndex characterSpacingCharacterRunIndex = 0u;
1086
1087   // check tag reference
1088   int colorTagReference            = 0u;
1089   int fontTagReference             = 0u;
1090   int iTagReference                = 0u;
1091   int bTagReference                = 0u;
1092   int uTagReference                = 0u;
1093   int backgroundTagReference       = 0u;
1094   int spanTagReference             = 0u;
1095   int sTagReference                = 0u;
1096   int pTagReference                = 0u;
1097   int characterSpacingTagReference = 0u;
1098
1099   // Give an initial default value to the model's vectors.
1100   markupProcessData.colorRuns.Reserve(DEFAULT_VECTOR_SIZE);
1101   markupProcessData.fontRuns.Reserve(DEFAULT_VECTOR_SIZE);
1102   markupProcessData.underlinedCharacterRuns.Reserve(DEFAULT_VECTOR_SIZE);
1103   markupProcessData.backgroundColorRuns.Reserve(DEFAULT_VECTOR_SIZE);
1104   markupProcessData.strikethroughCharacterRuns.Reserve(DEFAULT_VECTOR_SIZE);
1105   markupProcessData.characterSpacingCharacterRuns.Reserve(DEFAULT_VECTOR_SIZE);
1106
1107   // Get the mark-up string buffer.
1108   const char*       markupStringBuffer    = markupString.c_str();
1109   const char* const markupStringEndBuffer = markupStringBuffer + markupStringSize;
1110
1111   Tag            tag;
1112   CharacterIndex characterIndex = 0u;
1113   for(; markupStringBuffer < markupStringEndBuffer;)
1114   {
1115     tag.attributes.Clear();
1116     if(IsTag(markupStringBuffer,
1117              markupStringEndBuffer,
1118              tag))
1119     {
1120       if(TokenComparison(XHTML_COLOR_TAG, tag.buffer, tag.length))
1121       {
1122         ProcessTagForRun<ColorRun>(
1123           markupProcessData.colorRuns, styleStack, tag, characterIndex, colorRunIndex, colorTagReference, [](const Tag& tag, ColorRun& run) { ProcessColorTag(tag, run); });
1124       } // <color></color>
1125       else if(TokenComparison(XHTML_I_TAG, tag.buffer, tag.length))
1126       {
1127         ProcessTagForRun<FontDescriptionRun>(
1128           markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, iTagReference, [](const Tag&, FontDescriptionRun& fontRun) {
1129             fontRun.slant        = TextAbstraction::FontSlant::ITALIC;
1130             fontRun.slantDefined = true;
1131           });
1132       } // <i></i>
1133       else if(TokenComparison(XHTML_U_TAG, tag.buffer, tag.length))
1134       {
1135         ProcessTagForRun<UnderlinedCharacterRun>(
1136           markupProcessData.underlinedCharacterRuns, styleStack, tag, characterIndex, underlinedCharacterRunIndex, uTagReference, [](const Tag& tag, UnderlinedCharacterRun& run) { ProcessUnderlineTag(tag, run); });
1137       } // <u></u>
1138       else if(TokenComparison(XHTML_B_TAG, tag.buffer, tag.length))
1139       {
1140         ProcessTagForRun<FontDescriptionRun>(
1141           markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, bTagReference, [](const Tag&, FontDescriptionRun& fontRun) {
1142             fontRun.weight        = TextAbstraction::FontWeight::BOLD;
1143             fontRun.weightDefined = true;
1144           });
1145       } // <b></b>
1146       else if(TokenComparison(XHTML_FONT_TAG, tag.buffer, tag.length))
1147       {
1148         ProcessTagForRun<FontDescriptionRun>(
1149           markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, fontTagReference, [](const Tag& tag, FontDescriptionRun& fontRun) { ProcessFontTag(tag, fontRun); });
1150       } // <font></font>
1151       else if(TokenComparison(XHTML_ANCHOR_TAG, tag.buffer, tag.length))
1152       {
1153         /* Anchor */
1154         ProcessAnchorTag(markupProcessData, tag, characterIndex);
1155         /* Color */
1156         ProcessTagForRun<ColorRun>(
1157           markupProcessData.colorRuns, styleStack, tag, characterIndex, colorRunIndex, colorTagReference, [](const Tag& tag, ColorRun& run) {
1158             run.color = Color::BLUE;
1159             ProcessColorTag(tag, run);
1160           });
1161         /* Underline */
1162         ProcessTagForRun<UnderlinedCharacterRun>(
1163           markupProcessData.underlinedCharacterRuns, styleStack, tag, characterIndex, underlinedCharacterRunIndex, uTagReference, [](const Tag& tag, UnderlinedCharacterRun& run) {
1164             run.properties.color        = Color::BLUE;
1165             run.properties.colorDefined = true;
1166             ProcessUnderlineTag(tag, run);
1167           });
1168       } // <a href=https://www.tizen.org>tizen</a>
1169       else if(TokenComparison(XHTML_SHADOW_TAG, tag.buffer, tag.length))
1170       {
1171         // TODO: If !tag.isEndTag, then create a new shadow run.
1172         //       else Pop the top of the stack and set the number of characters of the run.
1173       } // <shadow></shadow>
1174       else if(TokenComparison(XHTML_GLOW_TAG, tag.buffer, tag.length))
1175       {
1176         // TODO: If !tag.isEndTag, then create a new glow run.
1177         //       else Pop the top of the stack and set the number of characters of the run.
1178       } // <glow></glow>
1179       else if(TokenComparison(XHTML_OUTLINE_TAG, tag.buffer, tag.length))
1180       {
1181         // TODO: If !tag.isEndTag, then create a new outline run.
1182         //       else Pop the top of the stack and set the number of characters of the run.
1183       } // <outline></outline>
1184       else if(TokenComparison(XHTML_ITEM_TAG, tag.buffer, tag.length))
1185       {
1186         ProcessItemTag(markupProcessData, tag, characterIndex);
1187       }
1188       else if(TokenComparison(XHTML_BACKGROUND_TAG, tag.buffer, tag.length))
1189       {
1190         ProcessTagForRun<ColorRun>(
1191           markupProcessData.backgroundColorRuns, styleStack, tag, characterIndex, backgroundRunIndex, backgroundTagReference, [](const Tag& tag, ColorRun& run) { ProcessBackground(tag, run); });
1192       }
1193       else if(TokenComparison(XHTML_SPAN_TAG, tag.buffer, tag.length))
1194       {
1195         ProcessSpanForRun(tag,
1196                           spanStack,
1197                           markupProcessData.colorRuns,
1198                           markupProcessData.fontRuns,
1199                           markupProcessData.underlinedCharacterRuns,
1200                           markupProcessData.backgroundColorRuns,
1201                           markupProcessData.strikethroughCharacterRuns,
1202                           markupProcessData.characterSpacingCharacterRuns,
1203                           colorRunIndex,
1204                           fontRunIndex,
1205                           underlinedCharacterRunIndex,
1206                           backgroundRunIndex,
1207                           strikethroughCharacterRunIndex,
1208                           characterSpacingCharacterRunIndex,
1209                           characterIndex,
1210                           spanTagReference);
1211       }
1212       else if(TokenComparison(XHTML_STRIKETHROUGH_TAG, tag.buffer, tag.length))
1213       {
1214         ProcessTagForRun<StrikethroughCharacterRun>(
1215           markupProcessData.strikethroughCharacterRuns, styleStack, tag, characterIndex, strikethroughCharacterRunIndex, sTagReference, [](const Tag& tag, StrikethroughCharacterRun& run) { ProcessStrikethroughTag(tag, run); });
1216       } // <s></s>
1217       else if(TokenComparison(XHTML_PARAGRAPH_TAG, tag.buffer, tag.length))
1218       {
1219         ProcessParagraphTag(markupProcessData, tag, (markupStringBuffer == markupStringEndBuffer), characterIndex);
1220         ProcessTagForRun<BoundedParagraphRun>(
1221           markupProcessData.boundedParagraphRuns, styleStack, tag, characterIndex, boundedParagraphRunIndex, pTagReference, [](const Tag& tag, BoundedParagraphRun& run) { ProcessAttributesOfParagraphTag(tag, run); });
1222       } // <p></p>
1223       else if(TokenComparison(XHTML_CHARACTER_SPACING_TAG, tag.buffer, tag.length))
1224       {
1225         ProcessTagForRun<CharacterSpacingCharacterRun>(
1226           markupProcessData.characterSpacingCharacterRuns, styleStack, tag, characterIndex, characterSpacingCharacterRunIndex, characterSpacingTagReference, [](const Tag& tag, CharacterSpacingCharacterRun& run) { ProcessCharacterSpacingTag(tag, run); });
1227       } // <char-spacing></char-spacing>
1228     }   // end if( IsTag() )
1229     else if(markupStringBuffer < markupStringEndBuffer)
1230     {
1231       ProcessMarkupStringBuffer(markupProcessData, markupStringBuffer, markupStringEndBuffer, characterIndex);
1232     }
1233   }
1234
1235   // Resize the model's vectors.
1236   ResizeModelVectors(markupProcessData, fontRunIndex, colorRunIndex, underlinedCharacterRunIndex, strikethroughCharacterRunIndex, backgroundRunIndex, boundedParagraphRunIndex, characterSpacingCharacterRunIndex);
1237
1238   // Handle the nested tags
1239   OverrideNestedUnderlinedCharacterRuns(markupProcessData.underlinedCharacterRuns);
1240   OverrideNestedStrikethroughCharacterRuns(markupProcessData.strikethroughCharacterRuns);
1241 }
1242
1243 } // namespace Text
1244
1245 } // namespace Toolkit
1246
1247 } // namespace Dali