2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/markup-processor.h>
22 #include <dali-toolkit/internal/text/character-set-conversion.h>
23 #include <dali-toolkit/internal/text/markup-processor-color.h>
24 #include <dali-toolkit/internal/text/markup-processor-font.h>
25 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
38 // HTML-ISH tag and attribute constants.
39 // Note they must be lower case in order to make the comparison to work
40 // as the parser converts all the read tags to lower case.
41 const std::string XHTML_COLOR_TAG("color");
42 const std::string XHTML_FONT_TAG("font");
43 const std::string XHTML_B_TAG("b");
44 const std::string XHTML_I_TAG("i");
45 const std::string XHTML_U_TAG("u");
46 const std::string XHTML_SHADOW_TAG("shadow");
47 const std::string XHTML_GLOW_TAG("glow");
48 const std::string XHTML_OUTLINE_TAG("outline");
50 const char LESS_THAN = '<';
51 const char GREATER_THAN = '>';
52 const char EQUAL = '=';
53 const char QUOTATION_MARK = '\'';
54 const char SLASH = '/';
55 const char BACK_SLASH = '\\';
57 const char WHITE_SPACE = 0x20; // ASCII value of the white space.
59 const unsigned int MAX_NUM_OF_ATTRIBUTES = 5u; ///< The font tag has the 'family', 'size' 'weight', 'width' and 'slant' attrubutes.
60 const unsigned int DEFAULT_VECTOR_SIZE = 16u; ///< Default size of run vectors.
63 * @brief Struct used to retrieve the style runs from the mark-up string.
67 typedef VectorBase::SizeType RunIndex;
69 Vector<RunIndex> stack; ///< Use a vector as a style stack. Stores the indices pointing where the run is stored inside the logical model.
70 unsigned int topIndex; ///< Points the top of the stack.
76 stack.Resize( DEFAULT_VECTOR_SIZE );
79 void Push( RunIndex index )
81 // Check if there is space inside the style stack.
82 const VectorBase::SizeType size = stack.Count();
83 if( topIndex >= size )
85 // Resize the style stack.
86 stack.Resize( 2u * size );
89 // Set the run index in the top of the stack.
90 *( stack.Begin() + topIndex ) = index;
92 // Reposition the pointer to the top of the stack.
98 // Pop the top of the stack.
100 return *( stack.Begin() + topIndex );
105 * @brief Initializes a font run description to its defaults.
107 * @param[in,out] fontRun The font description run to initialize.
109 void Initialize( FontDescriptionRun& fontRun )
111 fontRun.characterRun.characterIndex = 0u;
112 fontRun.characterRun.numberOfCharacters = 0u;
113 fontRun.familyName = NULL;
114 fontRun.familyLength = 0u;
115 fontRun.weight = TextAbstraction::FontWeight::NORMAL;
116 fontRun.width = TextAbstraction::FontWidth::NORMAL;
117 fontRun.slant = TextAbstraction::FontSlant::NORMAL;
119 fontRun.familyDefined = false;
120 fontRun.weightDefined = false;
121 fontRun.widthDefined = false;
122 fontRun.slantDefined = false;
123 fontRun.sizeDefined = false;
127 * @brief Splits the tag string into the tag name and its attributes.
129 * The attributes are stored in a vector in the tag.
131 * @param[in,out] tag The tag.
133 void ParseAttributes( Tag& tag )
135 tag.attributes.Resize( MAX_NUM_OF_ATTRIBUTES );
137 // Find first the tag name.
138 bool isQuotationOpen = false;
140 const char* tagBuffer = tag.buffer;
141 const char* const tagEndBuffer = tagBuffer + tag.length;
143 for( ; tagBuffer < tagEndBuffer; ++tagBuffer )
145 const char character = *tagBuffer;
146 if( WHITE_SPACE < character )
152 // Stops counting the length of the tag when a white space is found.
153 // @note a white space is the WHITE_SPACE character and anything below as 'tab', 'return' or 'control characters'.
157 SkipWhiteSpace( tagBuffer, tagEndBuffer );
159 // Find the attributes.
160 unsigned int attributeIndex = 0u;
161 const char* nameBuffer = NULL;
162 const char* valueBuffer = NULL;
163 Length nameLength = 0u;
164 Length valueLength = 0u;
166 bool addToNameValue = true;
167 Length numberOfWhiteSpace = 0u;
168 for( ; tagBuffer < tagEndBuffer; ++tagBuffer )
170 const char character = *tagBuffer;
171 if( ( WHITE_SPACE >= character ) && !isQuotationOpen )
173 if( NULL != valueBuffer )
175 // Remove white spaces at the end of the value.
176 valueLength -= numberOfWhiteSpace;
179 if( ( NULL != nameBuffer ) && ( NULL != valueBuffer ) )
181 // Every time a white space is found, a new attribute is created and stored in the attributes vector.
182 Attribute& attribute = *( tag.attributes.Begin() + attributeIndex );
185 attribute.nameBuffer = nameBuffer;
186 attribute.valueBuffer = valueBuffer;
187 attribute.nameLength = nameLength;
188 attribute.valueLength = valueLength;
195 addToNameValue = true; // next read characters will be added to the name.
198 else if( EQUAL == character ) // '='
200 addToNameValue = false; // next read characters will be added to the value.
201 SkipWhiteSpace( tagBuffer, tagEndBuffer );
203 else if( QUOTATION_MARK == character ) // '\''
205 // Do not add quotation marks to neither name nor value.
206 isQuotationOpen = !isQuotationOpen;
208 if( isQuotationOpen )
211 SkipWhiteSpace( tagBuffer, tagEndBuffer );
217 // Adds characters to the name or the value.
220 if( NULL == nameBuffer )
222 nameBuffer = tagBuffer;
228 if( isQuotationOpen )
230 if( WHITE_SPACE >= character )
232 ++numberOfWhiteSpace;
236 numberOfWhiteSpace = 0u;
239 if( NULL == valueBuffer )
241 valueBuffer = tagBuffer;
248 if( NULL != valueBuffer )
250 // Remove white spaces at the end of the value.
251 valueLength -= numberOfWhiteSpace;
254 if( ( NULL != nameBuffer ) && ( NULL != valueBuffer ) )
256 // Checks if the last attribute needs to be added.
257 Attribute& attribute = *( tag.attributes.Begin() + attributeIndex );
260 attribute.nameBuffer = nameBuffer;
261 attribute.valueBuffer = valueBuffer;
262 attribute.nameLength = nameLength;
263 attribute.valueLength = valueLength;
266 // Resize the vector of attributes.
267 tag.attributes.Resize( attributeIndex );
271 * @brief It parses a tag and its attributes if the given iterator @e it is pointing at a tag beginning.
273 * @param[in,out] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
274 * @param[in] markupStringEndBuffer Pointer to one character after the end of the mark-up string buffer.
275 * @param[out] tag The tag with its attributes.
277 * @return @e true if the iterator @e it is pointing a mark-up tag. Otherwise @e false.
279 bool IsTag( const char*& markupStringBuffer,
280 const char* const markupStringEndBuffer,
284 bool isQuotationOpen = false;
285 bool attributesFound = false;
286 tag.isEndTag = false;
288 const char character = *markupStringBuffer;
289 if( LESS_THAN == character ) // '<'
294 // if the iterator is pointing to a '<' character, then check if it's a mark-up tag is needed.
295 ++markupStringBuffer;
296 if( markupStringBuffer < markupStringEndBuffer )
298 SkipWhiteSpace( markupStringBuffer, markupStringEndBuffer );
300 for( ; ( !isTag ) && ( markupStringBuffer < markupStringEndBuffer ); ++markupStringBuffer )
302 const char character = *markupStringBuffer;
304 if( SLASH == character ) // '/'
306 // if the tag has a '/' then it's an end or empty tag.
309 if( ( markupStringBuffer + 1u < markupStringEndBuffer ) && ( WHITE_SPACE >= *( markupStringBuffer + 1u ) ) && ( !isQuotationOpen ) )
311 ++markupStringBuffer;
312 SkipWhiteSpace( markupStringBuffer, markupStringEndBuffer );
313 --markupStringBuffer;
316 else if( GREATER_THAN == character ) // '>'
320 else if( QUOTATION_MARK == character )
322 isQuotationOpen = !isQuotationOpen;
325 else if( WHITE_SPACE >= character ) // ' '
327 // If the tag contains white spaces then it may have attributes.
328 if( !isQuotationOpen )
330 attributesFound = true;
336 if( NULL == tag.buffer )
338 tag.buffer = markupStringBuffer;
341 // If it's not any of the 'special' characters then just add it to the tag string.
347 // If the tag string has white spaces, then parse the attributes is needed.
348 if( attributesFound )
350 ParseAttributes( tag );
359 void ProcessMarkupString( const std::string& markupString, MarkupProcessData& markupProcessData )
361 // Reserve space for the plain text.
362 const Length markupStringSize = markupString.size();
363 markupProcessData.markupProcessedText.reserve( markupStringSize );
365 // Stores a struct with the index to the first character of the run, the type of run and its parameters.
366 StyleStack styleStack;
368 // Points the next free position in the vector of runs.
369 StyleStack::RunIndex colorRunIndex = 0u;
370 StyleStack::RunIndex fontRunIndex = 0u;
372 // Give an initial default value to the model's vectors.
373 markupProcessData.colorRuns.Reserve( DEFAULT_VECTOR_SIZE );
374 markupProcessData.fontRuns.Reserve( DEFAULT_VECTOR_SIZE );
376 // Get the mark-up string buffer.
377 const char* markupStringBuffer = markupString.c_str();
378 const char* const markupStringEndBuffer = markupStringBuffer + markupStringSize;
381 CharacterIndex characterIndex = 0u;
382 for( ; markupStringBuffer < markupStringEndBuffer; )
384 if( IsTag( markupStringBuffer,
385 markupStringEndBuffer,
388 if( TokenComparison( XHTML_COLOR_TAG, tag.buffer, tag.length ) )
392 // Create a new color run.
394 colorRun.characterRun.numberOfCharacters = 0u;
396 // Set the start character index.
397 colorRun.characterRun.characterIndex = characterIndex;
399 // Fill the run with the attributes.
400 ProcessColorTag( tag, colorRun );
402 // Push the color run in the logical model.
403 markupProcessData.colorRuns.PushBack( colorRun );
405 // Push the index of the run into the stack.
406 styleStack.Push( colorRunIndex );
408 // Point the next color run.
413 // Pop the top of the stack and set the number of characters of the run.
414 ColorRun& colorRun = *( markupProcessData.colorRuns.Begin() + styleStack.Pop() );
415 colorRun.characterRun.numberOfCharacters = characterIndex - colorRun.characterRun.characterIndex;
418 else if( TokenComparison( XHTML_I_TAG, tag.buffer, tag.length ) )
422 // Create a new font run.
423 FontDescriptionRun fontRun;
424 Initialize( fontRun );
426 // Fill the run with the parameters.
427 fontRun.characterRun.characterIndex = characterIndex;
428 fontRun.slant = TextAbstraction::FontSlant::ITALIC;
429 fontRun.slantDefined = true;
431 // Push the font run in the logical model.
432 markupProcessData.fontRuns.PushBack( fontRun );
434 // Push the index of the run into the stack.
435 styleStack.Push( fontRunIndex );
437 // Point the next free font run.
442 // Pop the top of the stack and set the number of characters of the run.
443 FontDescriptionRun& fontRun = *( markupProcessData.fontRuns.Begin() + styleStack.Pop() );
444 fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
447 else if( TokenComparison( XHTML_U_TAG, tag.buffer, tag.length ) )
451 // Create a new underline run.
455 // Pop the top of the stack and set the number of characters of the run.
458 else if( TokenComparison( XHTML_B_TAG, tag.buffer, tag.length ) )
462 // Create a new font run.
463 FontDescriptionRun fontRun;
464 Initialize( fontRun );
466 // Fill the run with the parameters.
467 fontRun.characterRun.characterIndex = characterIndex;
468 fontRun.weight = TextAbstraction::FontWeight::BOLD;
469 fontRun.weightDefined = true;
471 // Push the font run in the logical model.
472 markupProcessData.fontRuns.PushBack( fontRun );
474 // Push the index of the run into the stack.
475 styleStack.Push( fontRunIndex );
477 // Point the next free font run.
482 // Pop the top of the stack and set the number of characters of the run.
483 FontDescriptionRun& fontRun = *( markupProcessData.fontRuns.Begin() + styleStack.Pop() );
484 fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
487 else if( TokenComparison( XHTML_FONT_TAG, tag.buffer, tag.length ) )
491 // Create a new font run.
492 FontDescriptionRun fontRun;
493 Initialize( fontRun );
495 // Fill the run with the parameters.
496 fontRun.characterRun.characterIndex = characterIndex;
498 ProcessFontTag( tag, fontRun );
500 // Push the font run in the logical model.
501 markupProcessData.fontRuns.PushBack( fontRun );
503 // Push the index of the run into the stack.
504 styleStack.Push( fontRunIndex );
506 // Point the next free font run.
511 // Pop the top of the stack and set the number of characters of the run.
512 FontDescriptionRun& fontRun = *( markupProcessData.fontRuns.Begin() + styleStack.Pop() );
513 fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
516 else if( TokenComparison( XHTML_SHADOW_TAG, tag.buffer, tag.length ) )
520 // Create a new shadow run.
524 // Pop the top of the stack and set the number of characters of the run.
526 } // <shadow></shadow>
527 else if( TokenComparison( XHTML_GLOW_TAG, tag.buffer, tag.length ) )
531 // Create a new glow run.
535 // Pop the top of the stack and set the number of characters of the run.
538 else if( TokenComparison( XHTML_OUTLINE_TAG, tag.buffer, tag.length ) )
542 // Create a new outline run.
546 // Pop the top of the stack and set the number of characters of the run.
548 } // <outline></outline>
549 } // end if( IsTag() )
552 unsigned char character = *markupStringBuffer;
554 if( ( BACK_SLASH == character ) && ( markupStringBuffer + 1u < markupStringEndBuffer ) )
556 // Adding < or > special character.
557 const unsigned char nextCharacter = *( markupStringBuffer + 1u );
558 if( ( LESS_THAN == nextCharacter ) || ( GREATER_THAN == nextCharacter ) )
560 character = nextCharacter;
561 ++markupStringBuffer;
565 const unsigned char numberOfBytes = GetUtf8Length( character );
567 markupProcessData.markupProcessedText.push_back( character );
568 for( unsigned char i = 1u; i < numberOfBytes; ++i )
570 ++markupStringBuffer;
571 markupProcessData.markupProcessedText.push_back( *markupStringBuffer );
575 ++markupStringBuffer;
579 // Resize the model's vectors.
580 if( 0u == fontRunIndex )
582 markupProcessData.fontRuns.Clear();
586 markupProcessData.fontRuns.Resize( fontRunIndex );
589 if( 0u == colorRunIndex )
591 markupProcessData.colorRuns.Clear();
595 markupProcessData.colorRuns.Resize( colorRunIndex );
601 } // namespace Toolkit