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