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