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