f26a04be0e8781948f2b010213921f43f8ea384a
[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 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 void GetStyledTextArray( const std::string& markupString, StyledTextArray& styledTextArray, bool scanForMarkup )
546 {
547   styledTextArray.clear();
548
549   if ( !scanForMarkup )
550   {
551     const Text text( markupString );
552     const std::size_t size = text.GetLength();
553
554     styledTextArray.resize( size, StyledText( Text(), TextStyle() ) );
555
556     std::size_t index = 0;
557     for( StyledTextArray::iterator it = styledTextArray.begin(), endIt = styledTextArray.end(); it != endIt; ++it, ++index )
558     {
559       StyledText& styledText( *it );
560
561       styledText.mText.Append( text[index] );
562     }
563     return;
564   }
565
566   TextStyle defaultStyle;
567   std::stack<TextStyle> styleStack;
568
569   styleStack.push( defaultStyle );
570   TextStyle currentStyle = styleStack.top();
571   std::string textToBeStored;
572   TextStyle styleToBeStored( currentStyle );
573   for( std::string::const_iterator it = markupString.begin(), endIt = markupString.end(); it != endIt; ++it )
574   {
575     std::string tag;
576     bool isEndTag = false;
577     std::vector<Property> tagProperties;
578     if( IsTag( it, endIt, tag, isEndTag, tagProperties ) )
579     {
580       if( CaseInsensitiveComparison( XHTML_I_TAG, tag ) )
581       {
582         if( !isEndTag )
583         {
584           TextStyle newStyle( currentStyle );
585           newStyle.SetItalics( true );
586           styleStack.push( newStyle );
587           currentStyle = styleStack.top();
588         }
589         else
590         {
591           styleStack.pop();
592           currentStyle = styleStack.top();
593         }
594       } // <i></i>
595       else if( CaseInsensitiveComparison( XHTML_U_TAG, tag ) )
596       {
597         if( !isEndTag )
598         {
599           TextStyle newStyle( currentStyle );
600           newStyle.SetUnderline( true );
601           styleStack.push( newStyle );
602           currentStyle = styleStack.top();
603         }
604         else
605         {
606           styleStack.pop();
607           currentStyle = styleStack.top();
608         }
609       } // <u></u>
610       else if( CaseInsensitiveComparison( XHTML_B_TAG, tag ) )
611       {
612         if( !isEndTag )
613         {
614           TextStyle newStyle( currentStyle );
615           newStyle.SetWeight( TextStyle::BOLD );
616           styleStack.push( newStyle );
617           currentStyle = styleStack.top();
618         }
619         else
620         {
621           styleStack.pop();
622           currentStyle = styleStack.top();
623         }
624       } // <b></b>
625       else if( CaseInsensitiveComparison( XHTML_BR_TAG, tag ) )
626       {
627         if( isEndTag )
628         {
629           AddText( textToBeStored, styleToBeStored, styledTextArray );
630           AddNewLineChar( currentStyle, styledTextArray );
631           textToBeStored.clear();
632         }
633       } // <br/>
634       else if( CaseInsensitiveComparison( XHTML_FONT_TAG, tag ) )
635       {
636         if( !isEndTag )
637         {
638           TextStyle newStyle( currentStyle );
639           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
640           {
641             const Property& property( *it );
642             if( CaseInsensitiveComparison( XHTML_FACE_PROPERTY, property.name ) )
643             {
644               newStyle.SetFontName( property.value );
645             }
646             else if( CaseInsensitiveComparison( XHTML_STYLE_PROPERTY, property.name ) )
647             {
648               newStyle.SetFontStyle( property.value );
649             }
650             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
651             {
652               Vector4 color;
653               ColorStringToVector4( property.value, color );
654               newStyle.SetTextColor( color );
655             }
656             else if( CaseInsensitiveComparison( XHTML_SIZE_PROPERTY, property.name ) )
657             {
658               newStyle.SetFontPointSize( PointSize( StringToFloat( property.value ) ) );
659             }
660           }
661           styleStack.push( newStyle );
662           currentStyle = styleStack.top();
663         }
664         else
665         {
666           styleStack.pop();
667           currentStyle = styleStack.top();
668         }
669       } // <font></font>
670       else if( CaseInsensitiveComparison( XHTML_SHADOW_TAG, tag ) )
671       {
672         if( !isEndTag )
673         {
674           TextStyle newStyle( currentStyle );
675           Vector4 color( TextStyle::DEFAULT_SHADOW_COLOR );
676           Vector2 offset( TextStyle::DEFAULT_SHADOW_OFFSET );
677           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
678           {
679             const Property& property( *it );
680             if( CaseInsensitiveComparison( XHTML_PARAM_X_PROPERTY, property.name ) )
681             {
682               offset.x = StringToFloat( property.value );
683             }
684             else if( CaseInsensitiveComparison( XHTML_PARAM_Y_PROPERTY, property.name ) )
685             {
686               offset.y = StringToFloat( property.value );
687             }
688             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
689             {
690               ColorStringToVector4( property.value, color );
691             }
692           }
693           newStyle.SetShadow( true, color, offset );
694           styleStack.push( newStyle );
695           currentStyle = styleStack.top();
696         }
697         else
698         {
699           styleStack.pop();
700           currentStyle = styleStack.top();
701         }
702       } // <shadow></shadow>
703       else if( CaseInsensitiveComparison( XHTML_GLOW_TAG, tag ) )
704       {
705         if( !isEndTag )
706         {
707           TextStyle newStyle( currentStyle );
708           Vector4 color( TextStyle::DEFAULT_GLOW_COLOR );
709           float intensity = TextStyle::DEFAULT_GLOW_INTENSITY;
710           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
711           {
712             const Property& property( *it );
713             if( CaseInsensitiveComparison( XHTML_PARAM_PROPERTY, property.name ) )
714             {
715               intensity = StringToFloat( property.value );
716             }
717             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
718             {
719               ColorStringToVector4( property.value, color );
720             }
721           }
722           newStyle.SetGlow( true, color, intensity );
723           styleStack.push( newStyle );
724           currentStyle = styleStack.top();
725         }
726         else
727         {
728           styleStack.pop();
729           currentStyle = styleStack.top();
730         }
731       } // <glow></glow>
732       else if( CaseInsensitiveComparison( XHTML_OUTLINE_TAG, tag ) )
733       {
734         if( !isEndTag )
735         {
736           TextStyle newStyle( currentStyle );
737           Vector4 color( TextStyle::DEFAULT_OUTLINE_COLOR );
738           Vector2 thickness( TextStyle::DEFAULT_OUTLINE_THICKNESS );
739           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
740           {
741             const Property& property( *it );
742             if( CaseInsensitiveComparison( XHTML_PARAM_X_PROPERTY, property.name ) )
743             {
744               thickness.x = StringToFloat( property.value );
745             }
746             else if( CaseInsensitiveComparison( XHTML_PARAM_Y_PROPERTY, property.name ) )
747             {
748               thickness.y = StringToFloat( property.value );
749             }
750             else if( CaseInsensitiveComparison( XHTML_COLOR_PROPERTY, property.name ) )
751             {
752               ColorStringToVector4( property.value, color );
753             }
754           }
755           newStyle.SetOutline( true, color, thickness );
756           styleStack.push( newStyle );
757           currentStyle = styleStack.top();
758         }
759         else
760         {
761           styleStack.pop();
762           currentStyle = styleStack.top();
763         }
764       } // <outline></outline>
765       else if( CaseInsensitiveComparison( XHTML_SMOOTH_EDGE_TAG, tag ) )
766       {
767         if( !isEndTag )
768         {
769           TextStyle newStyle( currentStyle );
770           for( std::vector<Property>::const_iterator it = tagProperties.begin(), endIt = tagProperties.end(); it != endIt; ++it )
771           {
772             const Property& property( *it );
773             if( CaseInsensitiveComparison( XHTML_PARAM_PROPERTY, property.name ) )
774             {
775               newStyle.SetSmoothEdge( StringToFloat( property.value ) );
776             }
777           }
778           styleStack.push( newStyle );
779           currentStyle = styleStack.top();
780         }
781         else
782         {
783           styleStack.pop();
784           currentStyle = styleStack.top();
785         }
786       } // <smooth></smooth>
787     } // end if( IsTag() )
788     else
789     {
790       char character( *it );
791
792       // Adding < or > special character.
793       if( ( BACK_SLASH == character ) && ( it + 1 != endIt ) )
794       {
795         const char nextChar( *( it + 1 ) );
796         if( ( LESS_THAN == nextChar ) || ( GREATER_THAN == nextChar ) )
797         {
798           character = nextChar;
799           ++it;
800         }
801       }
802       else if( ( LINE_SEPARATOR_CR == character ) && ( it + 1 != endIt ) )
803       {
804         if( LINE_SEPARATOR_LF == *( it + 1 ) )
805         {
806           character = LINE_SEPARATOR_LF;
807           ++it;
808         }
809       }
810
811       if( styleToBeStored != currentStyle )
812       {
813         if( !textToBeStored.empty() )
814         {
815           AddText( textToBeStored, styleToBeStored, styledTextArray );
816           textToBeStored.clear();
817         }
818         styleToBeStored = currentStyle;
819       }
820       textToBeStored.insert( textToBeStored.end(), character );
821     }
822   }
823   if( !textToBeStored.empty() )
824   {
825     AddText( textToBeStored, styleToBeStored, styledTextArray );
826     textToBeStored.clear();
827   }
828 }
829
830 void GetPlainString( const StyledTextArray& styledTextArray, std::string& plainString )
831 {
832   // First step is put all simultaneous characters with same style together.
833   for( StyledTextArray::const_iterator it = styledTextArray.begin(), endIt = styledTextArray.end(); it != endIt; ++it )
834   {
835     const StyledText& styledText( *it );
836     plainString += styledText.mText.GetText();
837   }
838 }
839
840 void GetMarkupString( const StyledTextArray& styledTextArray, std::string& markupString )
841 {
842   const std::string WHITE_SPACE( " " );
843
844   TextStyle previousStyle;
845   StyledText newStyledText;
846   StyledTextArray compressedStyledTextArray;
847
848   markupString.clear();
849
850   // First step is put all simultaneous characters with same style together.
851   for( StyledTextArray::const_iterator it = styledTextArray.begin(), endIt = styledTextArray.end(); it != endIt; ++it )
852   {
853     const StyledText& styledText( *it );
854
855     if( previousStyle != styledText.mStyle )
856     {
857       if( !newStyledText.mText.IsEmpty() )
858       {
859         compressedStyledTextArray.push_back( newStyledText );
860       }
861       newStyledText = StyledText();
862       newStyledText.mStyle = styledText.mStyle;
863     }
864
865     if( !styledText.mText.IsEmpty() )
866     {
867       const char character = styledText.mText.GetText()[0];
868       if( ( character == LESS_THAN ) || ( character == GREATER_THAN ) )
869       {
870         newStyledText.mText.Append( Text( std::string( &BACK_SLASH, 1 ) ) );
871       }
872     }
873
874     newStyledText.mText.Append( styledText.mText );
875
876     previousStyle = newStyledText.mStyle;
877   }
878
879   //Add the last characters.
880   if( !newStyledText.mText.IsEmpty() )
881   {
882     compressedStyledTextArray.push_back( newStyledText );
883   }
884
885   // Write markup string.
886   const std::string lineSeparatorStr( &LINE_SEPARATOR_LF );
887   const Text lineSeparator( lineSeparatorStr );
888
889   const TextStyle defaultStyle;
890   for( StyledTextArray::const_iterator it = compressedStyledTextArray.begin(), endIt = compressedStyledTextArray.end(); it != endIt; ++it )
891   {
892     const StyledText& styledText( *it );
893
894     bool isItalics = styledText.mStyle.GetItalics();
895     bool isBold = defaultStyle.GetWeight() != styledText.mStyle.GetWeight();
896     bool isUnderline = styledText.mStyle.GetUnderline();
897     bool hasFontFace = defaultStyle.GetFontName() != styledText.mStyle.GetFontName();
898     bool hasFontStyle = defaultStyle.GetFontStyle() != styledText.mStyle.GetFontStyle();
899     bool hasFontSize = fabsf( defaultStyle.GetFontPointSize() - styledText.mStyle.GetFontPointSize() ) > GetRangedEpsilon( defaultStyle.GetFontPointSize(), styledText.mStyle.GetFontPointSize() );
900     bool hasFontColor = defaultStyle.GetTextColor() != styledText.mStyle.GetTextColor();
901
902     bool hasSmooth = fabsf( defaultStyle.GetSmoothEdge() - styledText.mStyle.GetSmoothEdge() ) > GetRangedEpsilon( defaultStyle.GetSmoothEdge(), styledText.mStyle.GetSmoothEdge() );
903     bool hasShadowColor = defaultStyle.GetShadowColor() != styledText.mStyle.GetShadowColor();
904     bool hasShadowParams = defaultStyle.GetShadowOffset() != styledText.mStyle.GetShadowOffset();
905     bool hasGlowColor = defaultStyle.GetGlowColor() != styledText.mStyle.GetGlowColor();
906     bool hasGlowParams = fabsf( defaultStyle.GetGlowIntensity() - styledText.mStyle.GetGlowIntensity() ) > GetRangedEpsilon( defaultStyle.GetGlowIntensity(), styledText.mStyle.GetGlowIntensity() );
907     bool hasOutlineColor = defaultStyle.GetOutlineColor() != styledText.mStyle.GetOutlineColor();
908     bool hasOutlineParams = defaultStyle.GetOutlineThickness() != styledText.mStyle.GetOutlineThickness();
909
910     // Write font info.
911     if( hasFontFace || hasFontStyle || hasFontSize || hasFontColor )
912     {
913       markupString += LESS_THAN + XHTML_FONT_TAG;
914
915       if( hasFontFace )
916       {
917         markupString += WHITE_SPACE + XHTML_FACE_PROPERTY + EQUAL + QUOTATION_MARK + styledText.mStyle.GetFontName() + QUOTATION_MARK; // face=''
918       }
919
920       if( hasFontStyle )
921       {
922         markupString += WHITE_SPACE + XHTML_STYLE_PROPERTY + EQUAL + QUOTATION_MARK + styledText.mStyle.GetFontStyle() + QUOTATION_MARK; // style=''
923       }
924
925       if( hasFontSize )
926       {
927         markupString += WHITE_SPACE + XHTML_SIZE_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetFontPointSize() ) + QUOTATION_MARK; // size=''
928       }
929
930       if( hasFontColor )
931       {
932         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetTextColor() ) + QUOTATION_MARK; // color=''
933       }
934
935       markupString += GREATER_THAN;
936     } // <font>
937
938     // Write italics.
939     if( isItalics )
940     {
941       markupString += LESS_THAN + XHTML_I_TAG + GREATER_THAN;
942     } // <i>
943
944     // Write bold.
945     if( isBold )
946     {
947       markupString += LESS_THAN + XHTML_B_TAG + GREATER_THAN;
948     } // <b>
949
950     // Write underline.
951     if( isUnderline )
952     {
953       markupString += LESS_THAN + XHTML_U_TAG + GREATER_THAN;
954     } // <u>
955
956     // Write smooth.
957     if( hasSmooth )
958     {
959       markupString += LESS_THAN + XHTML_SMOOTH_EDGE_TAG + WHITE_SPACE + XHTML_PARAM_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetSmoothEdge() ) + QUOTATION_MARK + GREATER_THAN;
960     }
961
962     // Write shadow.
963     if( styledText.mStyle.GetShadow() )
964     {
965       markupString += LESS_THAN + XHTML_SHADOW_TAG;
966
967       if( hasShadowColor )
968       {
969         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetShadowColor() ) + QUOTATION_MARK;
970       }
971
972       if( hasShadowParams )
973       {
974         markupString += WHITE_SPACE + XHTML_PARAM_X_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetShadowOffset().x ) + QUOTATION_MARK;
975         markupString += WHITE_SPACE + XHTML_PARAM_Y_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetShadowOffset().y ) + QUOTATION_MARK;
976       }
977
978       markupString += GREATER_THAN;
979     }
980
981     // Write glow.
982     if( styledText.mStyle.GetGlow() )
983     {
984       markupString += LESS_THAN + XHTML_GLOW_TAG;
985
986       if( hasGlowColor )
987       {
988         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetGlowColor() ) + QUOTATION_MARK; // color=''
989       }
990
991       if( hasGlowParams )
992       {
993         markupString += WHITE_SPACE + XHTML_PARAM_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetGlowIntensity() ) + QUOTATION_MARK; // param=''
994       }
995
996       markupString += GREATER_THAN;
997     } // <glow>
998
999     // Write outline.
1000     if( styledText.mStyle.GetOutline() )
1001     {
1002       markupString += LESS_THAN + XHTML_OUTLINE_TAG;
1003
1004       if( hasOutlineColor )
1005       {
1006         markupString += WHITE_SPACE + XHTML_COLOR_PROPERTY + EQUAL + QUOTATION_MARK + Vector4ToColorString( styledText.mStyle.GetOutlineColor() ) + QUOTATION_MARK; // color = ''
1007       }
1008
1009       if( hasOutlineParams )
1010       {
1011         markupString += WHITE_SPACE + XHTML_PARAM_X_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetOutlineThickness().x ) + QUOTATION_MARK; // paramx=''
1012         markupString += WHITE_SPACE + XHTML_PARAM_Y_PROPERTY + EQUAL + QUOTATION_MARK + FloatToString( styledText.mStyle.GetOutlineThickness().y ) + QUOTATION_MARK; // paramy=''
1013       }
1014
1015       markupString += GREATER_THAN;
1016     } // <outline>
1017
1018     // Write text.
1019     if( styledText.mText[0] == lineSeparator[0] )
1020     {
1021       markupString += LESS_THAN + XHTML_BR_TAG + WHITE_SPACE + SLASH + GREATER_THAN; // <br />
1022     }
1023     else
1024     {
1025       markupString += styledText.mText.GetText();
1026     }
1027
1028     // Write outline close tag.
1029     if( styledText.mStyle.GetOutline() )
1030     {
1031       markupString += LESS_THAN + ( SLASH + XHTML_OUTLINE_TAG + GREATER_THAN ); // </outline>
1032     }
1033
1034     // Write glow close tag.
1035     if( styledText.mStyle.GetGlow() )
1036     {
1037       markupString += LESS_THAN + ( SLASH + XHTML_GLOW_TAG + GREATER_THAN ); // </glow>
1038     }
1039
1040     // Write shadow close tag.
1041     if( styledText.mStyle.GetShadow() )
1042     {
1043       markupString += LESS_THAN + ( SLASH + XHTML_SHADOW_TAG + GREATER_THAN ); // </shadow>
1044     }
1045
1046     // Write smooth close tag.
1047     if( hasSmooth )
1048     {
1049       markupString += LESS_THAN + ( SLASH + XHTML_SMOOTH_EDGE_TAG + GREATER_THAN ); // </smooth>
1050     }
1051
1052     // Write underline close tag.
1053     if( isUnderline )
1054     {
1055       markupString += LESS_THAN + ( SLASH + XHTML_U_TAG + GREATER_THAN ); // </u>
1056     }
1057
1058     // Write bold close tag.
1059     if( isBold )
1060     {
1061       markupString += LESS_THAN + ( SLASH + XHTML_B_TAG + GREATER_THAN ); // </b>
1062     }
1063
1064     // Write italics close tag.
1065     if( isItalics )
1066     {
1067       markupString += LESS_THAN + ( SLASH + XHTML_I_TAG + GREATER_THAN ); // </i>
1068     }
1069
1070     // Write font close tag.
1071     if( hasFontFace || hasFontStyle || hasFontSize || hasFontColor )
1072     {
1073       markupString += LESS_THAN + ( SLASH + XHTML_FONT_TAG + GREATER_THAN ); // </font>
1074     }
1075   }
1076 }
1077
1078 void SetTextStyle( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask )
1079 {
1080   if( !styledTextArray.empty() )
1081   {
1082     const size_t size = styledTextArray.size() - 1;
1083     SetTextStyleToRange( styledTextArray, style, mask, 0, size );
1084   }
1085 }
1086
1087 void SetTextStyle( const Text& text, StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask )
1088 {
1089   if( !text.IsEmpty() )
1090   {
1091     const size_t size = text.GetLength();
1092
1093     for( size_t i = 0; i < size; ++i )
1094     {
1095       StyledText styledText;
1096       styledText.mText = Text( text[i] );
1097       styledText.mStyle = style;
1098
1099       styledTextArray.push_back( styledText );
1100     }
1101
1102     SetTextStyleToRange( styledTextArray, style, mask, 0, size - 1 );
1103   }
1104 }
1105
1106 void SetTextStyleToRange( StyledTextArray& styledTextArray, const TextStyle& style, const TextStyle::Mask mask, const std::size_t begin, const std::size_t end )
1107 {
1108   const size_t size = styledTextArray.size();
1109   DALI_ASSERT_ALWAYS( begin < size );
1110   DALI_ASSERT_ALWAYS( end < size );
1111
1112   for( StyledTextArray::iterator it = styledTextArray.begin() + std::min(begin, end), endIt = styledTextArray.begin() + std::max(begin, end) + 1; it != endIt; ++it )
1113   {
1114     StyledText& styledText( *it );
1115
1116     styledText.mStyle.Copy( style, mask );
1117   } // for loop
1118 }
1119
1120 } // namespace MarkupProcessor
1121
1122 } // namespace Toolkit
1123
1124 } // namespace Dali