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