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