ed4cc57cc3c825534acde5e1c7e6b23505fd23f4
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / markup-processor / markup-processor.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // HEADER INCLUDE
18 #include <dali-toolkit/public-api/markup-processor/markup-processor.h>
19
20 // INTERNAL INCLUDES
21 #include <dali/public-api/text/text.h>
22
23
24 // EXTERNAL INCLUDES
25 #include <stack>
26 #include <sstream>
27
28 namespace Dali
29 {
30
31 namespace Toolkit
32 {
33
34 namespace MarkupProcessor
35 {
36
37 namespace
38 {
39 const std::string WEB_COLOR_TOKEN( "#" );
40 const std::string HEX_COLOR_TOKEN( "0x" );
41 const std::string ALPHA_ONE( "FF" );
42
43 const std::string BLACK_COLOR( "black" );
44 const std::string WHITE_COLOR( "white" );
45 const std::string RED_COLOR( "red" );
46 const std::string GREEN_COLOR( "green" );
47 const std::string BLUE_COLOR( "blue" );
48 const std::string YELLOW_COLOR( "yellow" );
49 const std::string MAGENTA_COLOR( "magenta" );
50 const std::string CYAN_COLOR( "cyan" );
51 const std::string TRANSPARENT_COLOR( "transparent" );
52
53 const std::string XHTML_B_TAG("b");
54 const std::string XHTML_I_TAG("i");
55 const std::string XHTML_U_TAG("u");
56 const std::string XHTML_BR_TAG("br");
57 const std::string XHTML_FONT_TAG("font");
58 const std::string XHTML_SHADOW_TAG("shadow");
59 const std::string XHTML_GLOW_TAG("glow");
60 const std::string XHTML_OUTLINE_TAG("outline");
61 const std::string XHTML_SMOOTH_EDGE_TAG("smooth");
62 const std::string XHTML_SIZE_PROPERTY("size");
63 const std::string XHTML_COLOR_PROPERTY("color");
64 const std::string XHTML_FACE_PROPERTY("face");
65 const std::string XHTML_STYLE_PROPERTY("style");
66 const std::string XHTML_PARAM_PROPERTY("param");
67 const std::string XHTML_PARAM_X_PROPERTY("paramx");
68 const std::string XHTML_PARAM_Y_PROPERTY("paramy");
69
70 const char LESS_THAN( '<' );
71 const char GREATER_THAN( '>' );
72 const char EQUAL( '=' );
73 const char QUOTATION_MARK( '\'');
74 const char LINE_SEPARATOR_CR( 0x0D ); // Carriage return character  CR
75 const char LINE_SEPARATOR_LF( 0x0A ); // New line character         LF
76 const char SLASH( '/' );
77 const char BACK_SLASH( '\\' );
78
79 const std::string LINE_SEPARATOR_LF_STRING(1 , LINE_SEPARATOR_LF); ///< a string with 1 line separator character
80 /**
81  * Stores a property pair: name, value.
82  */
83 struct Property
84 {
85   Property( const std::string& n, const std::string& v )
86   : name( n ),
87     value( v )
88   {}
89
90   std::string name;
91   std::string value;
92 };
93
94
95 /**
96  * Compare if two strings are equal.
97  *
98  * The comparison is case insensitive.
99  *
100  * @param[in] string1 First of the two strings to be compared.
101  * @param[in] string2 Second of the strings to be compared.
102  *
103  * @return \e true if both strings are equal (case insensitive).
104  */
105 bool CaseInsensitiveComparison( const std::string& string1, const std::string& string2 )
106 {
107   const std::size_t stringSize = string1.size();
108   if( stringSize != string2.size() )
109   {
110     // Early return. Strings have different sizes.
111     return false;
112   }
113
114   bool equal = true;
115   for( std::size_t index = 0; equal && ( index < stringSize ); ++index )
116   {
117     if( std::toupper( *( string1.begin() + index ) ) != std::toupper( *( string2.begin() + index ) ) )
118     {
119       equal = false;
120     }
121   }
122
123   return equal;
124 }
125
126 /**
127  * Converts a string into a float value.
128  * @param[in] floatStr A float packed inside a string.
129  * @return The float value.
130  */
131 float StringToFloat( const std::string& floatStr )
132 {
133   float ret = 0.f;
134
135   std::istringstream( floatStr ) >> ret;
136
137   return ret;
138 }
139
140 /**
141  * Converts a float into a string.
142  * @param[in] value. A float.
143  * @return The float packed into a string.
144  */
145 std::string FloatToString( float value )
146 {
147   std::ostringstream stream;
148   stream << value;
149
150   return stream.str();
151 }
152
153 /**
154  * Converts a string into an hexadecimal unsigned int.
155  * @param[in] uintStr An hexadecimal unsigned int packed inside a string.
156  * @return The hexadecimal value.
157  */
158 unsigned int StringToHex( const std::string& uintStr )
159 {
160   unsigned int ret = 0;
161
162   std::string str( uintStr );
163   if( uintStr.size() <= 8 )
164   {
165     str.insert( 2, std::string( "ff" ) );
166   }
167
168   std::istringstream( str ) >> std::hex >> ret;
169
170   return ret;
171 }
172
173 /**
174  * Converts an hexadecimal value into a string.
175  * @param[in] value. An hexadecimal value.
176  * @return The hexadecimal value packed inside a string.
177  */
178 std::string HexToString( unsigned int value )
179 {
180   std::ostringstream stream;
181   stream << std::hex << value;
182   return stream.str();
183 }
184
185 /**
186  * Converts an ARGB color packed in 4 byte unsigned int into a Vector4 color used in Dali.
187  * @param[in] color An ARGB color packed in an unsigned int.
188  * @param[out] retColor A Vector4 with the converted color.
189  */
190 void UintColorToVector4( unsigned int color, Vector4& retColor )
191 {
192   retColor.a = static_cast<float>( ( color & 0xFF000000 ) >> 24 ) / 255.f;
193   retColor.r = static_cast<float>( ( color & 0x00FF0000 ) >> 16 ) / 255.f;
194   retColor.g = static_cast<float>( ( color & 0x0000FF00 ) >> 8 ) / 255.f;
195   retColor.b = static_cast<float>( color & 0x000000FF ) / 255.f;
196 }
197
198 /**
199  * Converts an ARGB Vector4 color into a 4 byte packed inside an unsigned int.
200  * @param[in] color A Vector4 with an ARGB color.
201  * @return An unsigned int with the converted color.
202  */
203 unsigned int Vector4ColorToUint( const Vector4& color )
204 {
205   unsigned int retColor = 0;
206
207   retColor = static_cast<unsigned int>( color.a * 255.f ) << 24;
208   retColor += static_cast<unsigned int>( color.r * 255.f ) << 16;
209   retColor += static_cast<unsigned int>( color.g * 255.f ) << 8;
210   retColor += static_cast<unsigned int>( color.b * 255.f );
211
212   return retColor;
213 }
214
215 /**
216  * Converts a color packed inside a string into an ARGB Vector4 color.
217  * The string color could be in hexadecimal ( 0xFF0000FF ), webcolor ( #0000FF or #00F ) or some constant values:
218  * black, white, red, green, blue, yellow, magenta, cyan, transparent.
219  * @param[in] colorStr A color packed inside a string..
220  * @param[out] retColor A color packed inside a Vector4.
221  */
222 void ColorStringToVector4( const std::string& colorStr, Vector4& retColor )
223 {
224   std::string subStr1 = colorStr.substr( 0, 1 );
225   std::string subStr2 = colorStr.substr( 0, 2 );
226
227   if( WEB_COLOR_TOKEN == subStr1 )
228   {
229     std::string webColor( colorStr.begin() + 1, colorStr.end() );
230     if( 3 == webColor.size() )                      // 3 component web color #F00 (red)
231     {
232       webColor.insert( 2, &( webColor[2] ), 1 );
233       webColor.insert( 1, &( webColor[1] ), 1 );
234       webColor.insert( 0, &( webColor[0] ), 1 );
235       webColor.insert( 0, ALPHA_ONE );
236     }
237     else if( 6 == webColor.size() )                 // 6 component web color #FF0000 (red)
238     {
239       webColor.insert( 0, ALPHA_ONE );
240     }
241     webColor.insert( 0, HEX_COLOR_TOKEN );
242
243     UintColorToVector4( StringToHex( webColor ), retColor );
244   }
245   else if( CaseInsensitiveComparison( HEX_COLOR_TOKEN, subStr2 ) )
246   {
247     UintColorToVector4( StringToHex( colorStr ), retColor );
248   }
249   else if( CaseInsensitiveComparison( BLACK_COLOR, colorStr ) )
250   {
251     retColor = Color::BLACK;
252   }
253   else if( CaseInsensitiveComparison( WHITE_COLOR, colorStr ) )
254   {
255     retColor = Color::WHITE;
256   }
257   else if( CaseInsensitiveComparison( RED_COLOR, colorStr ) )
258   {
259     retColor = Color::RED;
260   }
261   else if( CaseInsensitiveComparison( GREEN_COLOR, colorStr ) )
262   {
263     retColor = Color::GREEN;
264   }
265   else if( CaseInsensitiveComparison( BLUE_COLOR, colorStr ) )
266   {
267     retColor = Color::BLUE;
268   }
269   else if( CaseInsensitiveComparison( YELLOW_COLOR, colorStr ) )
270   {
271     retColor = Color::YELLOW;
272   }
273   else if( CaseInsensitiveComparison( MAGENTA_COLOR, colorStr ) )
274   {
275     retColor = Color::MAGENTA;
276   }
277   else if( CaseInsensitiveComparison( CYAN_COLOR, colorStr ) )
278   {
279     retColor = Color::CYAN;
280   }
281   else if( CaseInsensitiveComparison( TRANSPARENT_COLOR, colorStr ) )
282   {
283     retColor = Color::TRANSPARENT;
284   }
285 }
286
287 /**
288  * Converts a color packed inside a Vector4 into a string.
289  * The string color could be in hexadecimal ( 0xFF0000FF ), webcolor ( #0000FF or #00F ) or some constant values:
290  * black, white, red, green, blue, yellow, magenta, cyan, transparent.
291  * @param[in] color A color packed inside a Vector4
292  * @return The color packed inside a string.
293  */
294 std::string Vector4ToColorString( const Vector4& color )
295 {
296   std::string colorStr;
297
298   if( Color::BLACK == color )
299   {
300     colorStr = BLACK_COLOR;
301   }
302   else if( Color::WHITE == color )
303   {
304     colorStr = WHITE_COLOR;
305   }
306   else if( Color::RED == color )
307   {
308     colorStr = RED_COLOR;
309   }
310   else if( Color::GREEN == color )
311   {
312     colorStr = GREEN_COLOR;
313   }
314   else if( Color::BLUE == color )
315   {
316     colorStr = BLUE_COLOR;
317   }
318   else if( Color::YELLOW == color )
319   {
320     colorStr = YELLOW_COLOR;
321   }
322   else if( Color::MAGENTA == color )
323   {
324     colorStr = MAGENTA_COLOR;
325   }
326   else if( Color::CYAN == color )
327   {
328     colorStr = CYAN_COLOR;
329   }
330   else if( Color::TRANSPARENT == color )
331   {
332     colorStr = TRANSPARENT_COLOR;
333   }
334   else
335   {
336     colorStr = HEX_COLOR_TOKEN + HexToString( Vector4ColorToUint( color ) );
337   }
338
339   return colorStr;
340 }
341
342 /**
343  * Skips any unnecessary white space.
344  * @param[in,out] it Iterator pointing to the current character of the markup string which is being parsed.
345  * @param[in] endIt Iterator pointing to the end of the markup string.
346  */
347 void SkipWhiteSpace( std::string::const_iterator& it, const std::string::const_iterator& endIt )
348 {
349   bool found = false;
350   for( ; ( !found ) && ( it != endIt ); ++it )
351   {
352     if( !isspace( *it ) )
353     {
354       found = true;
355       --it;
356     }
357   }
358 }
359
360 /**
361  * Adds a line separator 'LF' character to the given styled text array.
362  * @param[in] style The current style.
363  * @param[in,out] styledTextArray The given styled text array.
364  */
365 void AddNewLineChar( const TextStyle& style, StyledTextArray& styledTextArray )
366 {
367   const Text text( LINE_SEPARATOR_LF_STRING );
368   styledTextArray.push_back( StyledText( text, style ) );
369 }
370
371 /**
372  * Adds text to the given styled text array.
373  * It splits the text in characters.
374  * @param[in] textToBeStored The text to be stored.
375  * @param[in] styleToBeStored The current style.
376  * @param[in,out] styledTextArray The given styled text array.
377  */
378 void AddText( const std::string& textToBeStored, const TextStyle& styleToBeStored, StyledTextArray& styledTextArray )
379 {
380   const Text text( textToBeStored );
381   for( size_t i = 0, length = text.GetLength(); i < length; ++i )
382   {
383     styledTextArray.push_back( StyledText( Text( text[i] ), styleToBeStored ) );
384   }
385 }
386
387 /**
388  * Splits the tag string into the tag name and its properties.
389  * @param[in] tag The tag string with its pairs property, value.
390  * @param[out] tagName The name of the tag.
391  * @param[out] properties Vector of properties.
392  */
393 void ParseProperties( const std::string& tag, std::string& tagName, std::vector<Property>& properties )
394 {
395   // Find first the tag name.
396   bool found = false;
397   bool isQuotationOpen = false;
398   std::string::const_iterator it, endIt;
399   for( it = tag.begin(), endIt = tag.end(); ( !found ) && ( it != endIt ); ++it )
400   {
401     const char character( *it );
402     if( !isspace( character ) )
403     {
404       tagName += character;
405     }
406     else
407     {
408       found = true;
409     }
410   }
411   SkipWhiteSpace( it, endIt );
412
413   // Find the properties.
414   std::string name;
415   std::string value;
416   bool addToNameValue = true;
417   for( ; it != endIt; ++it )
418   {
419     const char character( *it );
420     if( isspace( character ) && !isQuotationOpen )
421     {
422       if( !name.empty() && !value.empty() )
423       {
424         // Every time a white space is found, a new property is created and stored in the properties vector.
425         properties.push_back( Property( name, value ) );
426         name .clear();
427         value.clear();
428         addToNameValue = true; // next read characters will be added to the name.
429       }
430     }
431     else if( EQUAL == character ) // '='
432     {
433       addToNameValue = false; // next read characters will be added to the value.
434       SkipWhiteSpace( it, endIt );
435     }
436     else if( QUOTATION_MARK == character ) // '\''
437     {
438       // Do not add quotation marks to neither name nor value.
439       isQuotationOpen = !isQuotationOpen;
440     }
441     else
442     {
443       // Adds characters to the name or the value.
444       if( addToNameValue )
445       {
446         name += character;
447       }
448       else
449       {
450         value += character;
451       }
452     }
453   }
454   if( !name.empty() && !value.empty() )
455   {
456     // Checks if the last property needs to be added.
457     properties.push_back( Property( name, value ) );
458   }
459 }
460
461 /**
462  * It parses a tag and its properties if the given iterator \e it is pointing at a tag beginning.
463  * @param[in,out] it Iterator pointing to the current character of the markup string which is being parsed.
464  * @param[in] endIt Iterator pointing to the end of the markup string.
465  * @param[out] tag Name of the tag.
466  * @param[out] isEndTag Whether the tag is and end tag i.e </tag_name> or not.
467  * @param[out] properties The vector with tag properties.
468  * @return \e true if the iterator \e it is pointing a markup tag. Otherwise \e false.
469  */
470 bool IsTag( std::string::const_iterator& it, const std::string::const_iterator& endIt, std::string& tag, bool& isEndTag, std::vector<Property>& properties )
471 {
472   bool isTag = false;
473   bool isQuotationOpen = false;
474   bool propertiesFound = false;
475   std::string tagString;
476
477   const char character( *it );
478   if( LESS_THAN == character ) // '<'
479   {
480     // if the iterator is pointing to a '<' character, then check if it's a markup tag is needed.
481     ++it;
482     if( it != endIt )
483     {
484       SkipWhiteSpace( it, endIt );
485
486       for( ; ( !isTag ) && ( it != endIt ); ++it )
487       {
488         const char character( *it );
489
490         if( SLASH == character ) // '/'
491         {
492           // if the tag has a '/' then it's an end or empty tag.
493           isEndTag = true;
494
495           if( ( it + 1 != endIt ) && ( isspace( *( it + 1 ) ) )  && ( !isQuotationOpen ) )
496           {
497             ++it;
498             SkipWhiteSpace( it, endIt );
499             --it;
500           }
501         }
502         else if( GREATER_THAN == character ) // '>'
503         {
504           isTag = true;
505         }
506         else if(QUOTATION_MARK == character)
507         {
508           isQuotationOpen = !isQuotationOpen;
509           tagString += character;
510         }
511         else if( isspace( character ) ) // ' '
512         {
513           // If the tag contains white spaces then it may have properties.
514           if ( !isQuotationOpen )
515           {
516             propertiesFound = true;
517           }
518           tagString += character;
519         }
520         else
521         {
522           // If it's not any of the 'special' characters then just add it to the tag string.
523           tagString += character;
524         }
525       }
526       --it;
527     }
528   }
529
530   // If the tag string has white spaces, then parse the properties is needed.
531   if( propertiesFound )
532   {
533     ParseProperties( tagString, tag, properties );
534   }
535   else
536   {
537     tag = tagString;
538   }
539
540   return isTag;
541 }
542
543 } // namespace
544
545 static inline bool HasMarkup( const std::string& markupString )
546 {
547   // Reset counters
548   unsigned int lessThanCount = 0;
549   unsigned int greaterThanCount = 0;
550
551   // Check to see if any markup command surrounds are of equal number and not zero
552   for ( std::string::const_iterator it = markupString.begin(); it != markupString.end(); ++it )
553   {
554     if ( *it == LESS_THAN )
555     {
556       lessThanCount++;
557     }
558     else
559     {
560       if ( *it == GREATER_THAN )
561       {
562         greaterThanCount++;
563       }
564       else
565       {
566         if ( *it == BACK_SLASH )
567         {
568           return true;
569         }
570       }
571     }
572   }
573   if ( !lessThanCount || !greaterThanCount || lessThanCount != greaterThanCount )
574   {
575     return false;
576   }
577   return true;
578 }
579
580 void GetStyledTextArray( const std::string& markupString, StyledTextArray& styledTextArray, bool scanForMarkup )
581 {
582   styledTextArray.clear();
583
584   // Scan markup ( if necessary ) to see if the string contains any change in style from default?
585   if ( !scanForMarkup || !HasMarkup( markupString ) )
586   {
587     styledTextArray.push_back( StyledText( Text( markupString ), TextStyle() ) );
588     return;
589   }
590
591   TextStyle defaultStyle;
592   std::stack<TextStyle> styleStack;
593
594   styleStack.push( defaultStyle );
595   TextStyle currentStyle = styleStack.top();
596   std::string textToBeStored;
597   TextStyle styleToBeStored( currentStyle );
598   for( std::string::const_iterator it = markupString.begin(), endIt = markupString.end(); it != endIt; ++it )
599   {
600     std::string tag;
601     bool isEndTag = false;
602     std::vector<Property> tagProperties;
603     if( IsTag( it, endIt, tag, isEndTag, tagProperties ) )
604     {
605       if( CaseInsensitiveComparison( XHTML_I_TAG, tag ) )
606       {
607         if( !isEndTag )
608         {
609           TextStyle newStyle( currentStyle );
610           newStyle.SetItalics( true );
611           styleStack.push( newStyle );
612           currentStyle = styleStack.top();
613         }
614         else
615         {
616           styleStack.pop();
617           currentStyle = styleStack.top();
618         }
619       } // <i></i>
620       else if( CaseInsensitiveComparison( XHTML_U_TAG, tag ) )
621       {
622         if( !isEndTag )
623         {
624           TextStyle newStyle( currentStyle );
625           newStyle.SetUnderline( true );
626           styleStack.push( newStyle );
627           currentStyle = styleStack.top();
628         }
629         else
630         {
631           styleStack.pop();
632           currentStyle = styleStack.top();
633         }
634       } // <u></u>
635       else if( CaseInsensitiveComparison( XHTML_B_TAG, tag ) )
636       {
637         if( !isEndTag )
638         {
639           TextStyle newStyle( currentStyle );
640           newStyle.SetWeight( TextStyle::BOLD );
641           styleStack.push( newStyle );
642           currentStyle = styleStack.top();
643         }
644         else
645         {
646           styleStack.pop();
647           currentStyle = styleStack.top();
648         }
649       } // <b></b>
650       else if( CaseInsensitiveComparison( XHTML_BR_TAG, tag ) )
651       {
652         if( isEndTag )
653         {
654           AddText( textToBeStored, styleToBeStored, styledTextArray );
655           AddNewLineChar( currentStyle, styledTextArray );
656           textToBeStored.clear();
657         }
658       } // <br/>
659       else if( CaseInsensitiveComparison( XHTML_FONT_TAG, tag ) )
660       {
661         if( !isEndTag )
662         {
663           TextStyle newStyle( currentStyle );
664           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
665           {
666             const Property& property( *it );
667             if( CaseInsensitiveComparison( XHTML_FACE_PROPERTY, property.name ) )
668             {
669               newStyle.SetFontName( property.value );
670             }
671             else if( CaseInsensitiveComparison( XHTML_STYLE_PROPERTY, property.name ) )
672             {
673               newStyle.SetFontStyle( property.value );
674             }
675             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
676             {
677               Vector4 color;
678               ColorStringToVector4( property.value, color );
679               newStyle.SetTextColor( color );
680             }
681             else if( CaseInsensitiveComparison( XHTML_SIZE_PROPERTY, property.name ) )
682             {
683               newStyle.SetFontPointSize( PointSize( StringToFloat( property.value ) ) );
684             }
685           }
686           styleStack.push( newStyle );
687           currentStyle = styleStack.top();
688         }
689         else
690         {
691           styleStack.pop();
692           currentStyle = styleStack.top();
693         }
694       } // <font></font>
695       else if( CaseInsensitiveComparison( XHTML_SHADOW_TAG, tag ) )
696       {
697         if( !isEndTag )
698         {
699           TextStyle newStyle( currentStyle );
700           Vector4 color( TextStyle::DEFAULT_SHADOW_COLOR );
701           Vector2 offset( TextStyle::DEFAULT_SHADOW_OFFSET );
702           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
703           {
704             const Property& property( *it );
705             if( CaseInsensitiveComparison( XHTML_PARAM_X_PROPERTY, property.name ) )
706             {
707               offset.x = StringToFloat( property.value );
708             }
709             else if( CaseInsensitiveComparison( XHTML_PARAM_Y_PROPERTY, property.name ) )
710             {
711               offset.y = StringToFloat( property.value );
712             }
713             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
714             {
715               ColorStringToVector4( property.value, color );
716             }
717           }
718           newStyle.SetShadow( true, color, offset );
719           styleStack.push( newStyle );
720           currentStyle = styleStack.top();
721         }
722         else
723         {
724           styleStack.pop();
725           currentStyle = styleStack.top();
726         }
727       } // <shadow></shadow>
728       else if( CaseInsensitiveComparison( XHTML_GLOW_TAG, tag ) )
729       {
730         if( !isEndTag )
731         {
732           TextStyle newStyle( currentStyle );
733           Vector4 color( TextStyle::DEFAULT_GLOW_COLOR );
734           float intensity = TextStyle::DEFAULT_GLOW_INTENSITY;
735           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
736           {
737             const Property& property( *it );
738             if( CaseInsensitiveComparison( XHTML_PARAM_PROPERTY, property.name ) )
739             {
740               intensity = StringToFloat( property.value );
741             }
742             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
743             {
744               ColorStringToVector4( property.value, color );
745             }
746           }
747           newStyle.SetGlow( true, color, intensity );
748           styleStack.push( newStyle );
749           currentStyle = styleStack.top();
750         }
751         else
752         {
753           styleStack.pop();
754           currentStyle = styleStack.top();
755         }
756       } // <glow></glow>
757       else if( CaseInsensitiveComparison( XHTML_OUTLINE_TAG, tag ) )
758       {
759         if( !isEndTag )
760         {
761           TextStyle newStyle( currentStyle );
762           Vector4 color( TextStyle::DEFAULT_OUTLINE_COLOR );
763           Vector2 thickness( TextStyle::DEFAULT_OUTLINE_THICKNESS );
764           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
765           {
766             const Property& property( *it );
767             if( CaseInsensitiveComparison( XHTML_PARAM_X_PROPERTY, property.name ) )
768             {
769               thickness.x = StringToFloat( property.value );
770             }
771             else if( CaseInsensitiveComparison( XHTML_PARAM_Y_PROPERTY, property.name ) )
772             {
773               thickness.y = StringToFloat( property.value );
774             }
775             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
776             {
777               ColorStringToVector4( property.value, color );
778             }
779           }
780           newStyle.SetOutline( true, color, thickness );
781           styleStack.push( newStyle );
782           currentStyle = styleStack.top();
783         }
784         else
785         {
786           styleStack.pop();
787           currentStyle = styleStack.top();
788         }
789       } // <outline></outline>
790       else if( CaseInsensitiveComparison( XHTML_SMOOTH_EDGE_TAG, tag ) )
791       {
792         if( !isEndTag )
793         {
794           TextStyle newStyle( currentStyle );
795           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
796           {
797             const Property& property( *it );
798             if( CaseInsensitiveComparison( XHTML_PARAM_PROPERTY, property.name ) )
799             {
800               newStyle.SetSmoothEdge( StringToFloat( property.value ) );
801             }
802           }
803           styleStack.push( newStyle );
804           currentStyle = styleStack.top();
805         }
806         else
807         {
808           styleStack.pop();
809           currentStyle = styleStack.top();
810         }
811       } // <smooth></smooth>
812     } // end if( IsTag() )
813     else
814     {
815       char character( *it );
816
817       // Adding < or > special character.
818       if( ( BACK_SLASH == character ) && ( it + 1 != endIt ) )
819       {
820         const char nextChar( *( it + 1 ) );
821         if( ( LESS_THAN == nextChar ) || ( GREATER_THAN == nextChar ) )
822         {
823           character = nextChar;
824           ++it;
825         }
826       }
827       else if( ( LINE_SEPARATOR_CR == character ) && ( it + 1 != endIt ) )
828       {
829         if( LINE_SEPARATOR_LF == *( it + 1 ) )
830         {
831           character = LINE_SEPARATOR_LF;
832           ++it;
833         }
834       }
835
836       if( styleToBeStored != currentStyle )
837       {
838         if( !textToBeStored.empty() )
839         {
840           AddText( textToBeStored, styleToBeStored, styledTextArray );
841           textToBeStored.clear();
842         }
843         styleToBeStored = currentStyle;
844       }
845       textToBeStored.insert( textToBeStored.end(), character );
846     }
847   }
848   if( !textToBeStored.empty() )
849   {
850     AddText( textToBeStored, styleToBeStored, styledTextArray );
851     textToBeStored.clear();
852   }
853 }
854
855 void GetPlainString( const StyledTextArray& styledTextArray, std::string& plainString )
856 {
857   // First step is put all simultaneous characters with same style together.
858   for( StyledTextArray::const_iterator it = styledTextArray.begin(), endIt = styledTextArray.end(); it != endIt; ++it )
859   {
860     const StyledText& styledText( *it );
861     plainString += styledText.mText.GetText();
862   }
863 }
864
865 void GetMarkupString( const StyledTextArray& styledTextArray, std::string& markupString )
866 {
867   const std::string WHITE_SPACE( " " );
868
869   TextStyle previousStyle;
870   StyledText newStyledText;
871   StyledTextArray compressedStyledTextArray;
872
873   markupString.clear();
874
875   // First step is put all simultaneous characters with same style together.
876   for( StyledTextArray::const_iterator it = styledTextArray.begin(), endIt = styledTextArray.end(); it != endIt; ++it )
877   {
878     const StyledText& styledText( *it );
879
880     if( previousStyle != styledText.mStyle )
881     {
882       if( !newStyledText.mText.IsEmpty() )
883       {
884         compressedStyledTextArray.push_back( newStyledText );
885       }
886       newStyledText = StyledText();
887       newStyledText.mStyle = styledText.mStyle;
888     }
889
890     if( !styledText.mText.IsEmpty() )
891     {
892       const char character = styledText.mText.GetText()[0];
893       if( ( character == LESS_THAN ) || ( character == GREATER_THAN ) )
894       {
895         newStyledText.mText.Append( Text( std::string( &BACK_SLASH, 1 ) ) );
896       }
897     }
898
899     newStyledText.mText.Append( styledText.mText );
900
901     previousStyle = newStyledText.mStyle;
902   }
903
904   //Add the last characters.
905   if( !newStyledText.mText.IsEmpty() )
906   {
907     compressedStyledTextArray.push_back( newStyledText );
908   }
909
910   // Write markup string.
911   const std::string lineSeparatorStr( &LINE_SEPARATOR_LF );
912   const Text lineSeparator( lineSeparatorStr );
913
914   const TextStyle defaultStyle;
915   for( StyledTextArray::const_iterator it = compressedStyledTextArray.begin(), endIt = compressedStyledTextArray.end(); it != endIt; ++it )
916   {
917     const StyledText& styledText( *it );
918
919     bool isItalics = styledText.mStyle.GetItalics();
920     bool isBold = defaultStyle.GetWeight() != styledText.mStyle.GetWeight();
921     bool isUnderline = styledText.mStyle.GetUnderline();
922     bool hasFontFace = defaultStyle.GetFontName() != styledText.mStyle.GetFontName();
923     bool hasFontStyle = defaultStyle.GetFontStyle() != styledText.mStyle.GetFontStyle();
924     bool hasFontSize = fabsf( defaultStyle.GetFontPointSize() - styledText.mStyle.GetFontPointSize() ) > GetRangedEpsilon( defaultStyle.GetFontPointSize(), styledText.mStyle.GetFontPointSize() );
925     bool hasFontColor = defaultStyle.GetTextColor() != styledText.mStyle.GetTextColor();
926
927     bool hasSmooth = fabsf( defaultStyle.GetSmoothEdge() - styledText.mStyle.GetSmoothEdge() ) > GetRangedEpsilon( defaultStyle.GetSmoothEdge(), styledText.mStyle.GetSmoothEdge() );
928     bool hasShadowColor = defaultStyle.GetShadowColor() != styledText.mStyle.GetShadowColor();
929     bool hasShadowParams = defaultStyle.GetShadowOffset() != styledText.mStyle.GetShadowOffset();
930     bool hasGlowColor = defaultStyle.GetGlowColor() != styledText.mStyle.GetGlowColor();
931     bool hasGlowParams = fabsf( defaultStyle.GetGlowIntensity() - styledText.mStyle.GetGlowIntensity() ) > GetRangedEpsilon( defaultStyle.GetGlowIntensity(), styledText.mStyle.GetGlowIntensity() );
932     bool hasOutlineColor = defaultStyle.GetOutlineColor() != styledText.mStyle.GetOutlineColor();
933     bool hasOutlineParams = defaultStyle.GetOutlineThickness() != styledText.mStyle.GetOutlineThickness();
934
935     // Write font info.
936     if( hasFontFace || hasFontStyle || hasFontSize || hasFontColor )
937     {
938       markupString += LESS_THAN + XHTML_FONT_TAG;
939
940       if( hasFontFace )
941       {
942         markupString += WHITE_SPACE + XHTML_FACE_PROPERTY + EQUAL + QUOTATION_MARK + styledText.mStyle.GetFontName() + QUOTATION_MARK; // face=''
943       }
944
945       if( hasFontStyle )
946       {
947         markupString += WHITE_SPACE + XHTML_STYLE_PROPERTY + EQUAL + QUOTATION_MARK + styledText.mStyle.GetFontStyle() + QUOTATION_MARK; // style=''
948       }
949
950       if( hasFontSize )
951       {
952         markupString += WHITE_SPACE + XHTML_SIZE_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetFontPointSize() ) + QUOTATION_MARK; // size=''
953       }
954
955       if( hasFontColor )
956       {
957         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetTextColor() ) + QUOTATION_MARK; // color=''
958       }
959
960       markupString += GREATER_THAN;
961     } // <font>
962
963     // Write italics.
964     if( isItalics )
965     {
966       markupString += LESS_THAN + XHTML_I_TAG + GREATER_THAN;
967     } // <i>
968
969     // Write bold.
970     if( isBold )
971     {
972       markupString += LESS_THAN + XHTML_B_TAG + GREATER_THAN;
973     } // <b>
974
975     // Write underline.
976     if( isUnderline )
977     {
978       markupString += LESS_THAN + XHTML_U_TAG + GREATER_THAN;
979     } // <u>
980
981     // Write smooth.
982     if( hasSmooth )
983     {
984       markupString += LESS_THAN + XHTML_SMOOTH_EDGE_TAG + WHITE_SPACE + XHTML_PARAM_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetSmoothEdge() ) + QUOTATION_MARK + GREATER_THAN;
985     }
986
987     // Write shadow.
988     if( styledText.mStyle.GetShadow() )
989     {
990       markupString += LESS_THAN + XHTML_SHADOW_TAG;
991
992       if( hasShadowColor )
993       {
994         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetShadowColor() ) + QUOTATION_MARK;
995       }
996
997       if( hasShadowParams )
998       {
999         markupString += WHITE_SPACE + XHTML_PARAM_X_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetShadowOffset().x ) + QUOTATION_MARK;
1000         markupString += WHITE_SPACE + XHTML_PARAM_Y_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetShadowOffset().y ) + QUOTATION_MARK;
1001       }
1002
1003       markupString += GREATER_THAN;
1004     }
1005
1006     // Write glow.
1007     if( styledText.mStyle.GetGlow() )
1008     {
1009       markupString += LESS_THAN + XHTML_GLOW_TAG;
1010
1011       if( hasGlowColor )
1012       {
1013         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetGlowColor() ) + QUOTATION_MARK; // color=''
1014       }
1015
1016       if( hasGlowParams )
1017       {
1018         markupString += WHITE_SPACE + XHTML_PARAM_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetGlowIntensity() ) + QUOTATION_MARK; // param=''
1019       }
1020
1021       markupString += GREATER_THAN;
1022     } // <glow>
1023
1024     // Write outline.
1025     if( styledText.mStyle.GetOutline() )
1026     {
1027       markupString += LESS_THAN + XHTML_OUTLINE_TAG;
1028
1029       if( hasOutlineColor )
1030       {
1031         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetOutlineColor() ) + QUOTATION_MARK; // color = ''
1032       }
1033
1034       if( hasOutlineParams )
1035       {
1036         markupString += WHITE_SPACE + XHTML_PARAM_X_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetOutlineThickness().x ) + QUOTATION_MARK; // paramx=''
1037         markupString += WHITE_SPACE + XHTML_PARAM_Y_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetOutlineThickness().y ) + QUOTATION_MARK; // paramy=''
1038       }
1039
1040       markupString += GREATER_THAN;
1041     } // <outline>
1042
1043     // Write text.
1044     if( styledText.mText[0] == lineSeparator[0] )
1045     {
1046       markupString += LESS_THAN + XHTML_BR_TAG + WHITE_SPACE + SLASH + GREATER_THAN; // <br />
1047     }
1048     else
1049     {
1050       markupString += styledText.mText.GetText();
1051     }
1052
1053     // Write outline close tag.
1054     if( styledText.mStyle.GetOutline() )
1055     {
1056       markupString += LESS_THAN + ( SLASH + XHTML_OUTLINE_TAG + GREATER_THAN ); // </outline>
1057     }
1058
1059     // Write glow close tag.
1060     if( styledText.mStyle.GetGlow() )
1061     {
1062       markupString += LESS_THAN + ( SLASH + XHTML_GLOW_TAG + GREATER_THAN ); // </glow>
1063     }
1064
1065     // Write shadow close tag.
1066     if( styledText.mStyle.GetShadow() )
1067     {
1068       markupString += LESS_THAN + ( SLASH + XHTML_SHADOW_TAG + GREATER_THAN ); // </shadow>
1069     }
1070
1071     // Write smooth close tag.
1072     if( hasSmooth )
1073     {
1074       markupString += LESS_THAN + ( SLASH + XHTML_SMOOTH_EDGE_TAG + GREATER_THAN ); // </smooth>
1075     }
1076
1077     // Write underline close tag.
1078     if( isUnderline )
1079     {
1080       markupString += LESS_THAN + ( SLASH + XHTML_U_TAG + GREATER_THAN ); // </u>
1081     }
1082
1083     // Write bold close tag.
1084     if( isBold )
1085     {
1086       markupString += LESS_THAN + ( SLASH + XHTML_B_TAG + GREATER_THAN ); // </b>
1087     }
1088
1089     // Write italics close tag.
1090     if( isItalics )
1091     {
1092       markupString += LESS_THAN + ( SLASH + XHTML_I_TAG + GREATER_THAN ); // </i>
1093     }
1094
1095     // Write font close tag.
1096     if( hasFontFace || hasFontStyle || hasFontSize || hasFontColor )
1097     {
1098       markupString += LESS_THAN + ( SLASH + XHTML_FONT_TAG + GREATER_THAN ); // </font>
1099     }
1100   }
1101 }
1102
1103 void SetTextStyle( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask )
1104 {
1105   if( !styledTextArray.empty() )
1106   {
1107     const size_t size = styledTextArray.size() - 1;
1108     SetTextStyleToRange( styledTextArray, style, mask, 0, size );
1109   }
1110 }
1111
1112 void SetTextStyle( const Text& text, StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask )
1113 {
1114   if( !text.IsEmpty() )
1115   {
1116     const size_t size = text.GetLength();
1117
1118     for( size_t i = 0; i < size; ++i )
1119     {
1120       StyledText styledText;
1121       styledText.mText = Text( text[i] );
1122       styledText.mStyle = style;
1123
1124       styledTextArray.push_back( styledText );
1125     }
1126
1127     SetTextStyleToRange( styledTextArray, style, mask, 0, size - 1 );
1128   }
1129 }
1130
1131 void SetTextStyleToRange( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end )
1132 {
1133   const size_t size = styledTextArray.size();
1134   DALI_ASSERT_ALWAYS( begin < size );
1135   DALI_ASSERT_ALWAYS( end < size );
1136
1137   for( StyledTextArray::iterator it = styledTextArray.begin() + std::min(begin, end), endIt = styledTextArray.begin() + std::max(begin, end) + 1; it != endIt; ++it )
1138   {
1139     StyledText& styledText( *it );
1140
1141     styledText.mStyle.Copy( style, mask );
1142   } // for loop
1143 }
1144
1145 } // namespace MarkupProcessor
1146
1147 } // namespace Toolkit
1148
1149 } // namespace Dali