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