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