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