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 Splits the tag string into the tag name and its attributes.
107 * The attributes are stored in a vector in the tag.
109 * @param[in,out] tag The tag.
111 void ParseAttributes( Tag& tag )
113 tag.attributes.Resize( MAX_NUM_OF_ATTRIBUTES );
115 // Find first the tag name.
116 bool isQuotationOpen = false;
118 const char* tagBuffer = tag.buffer;
119 const char* const tagEndBuffer = tagBuffer + tag.length;
121 for( ; tagBuffer < tagEndBuffer; ++tagBuffer )
123 const char character = *tagBuffer;
124 if( WHITE_SPACE < character )
130 // Stops counting the length of the tag when a white space is found.
131 // @note a white space is the WHITE_SPACE character and anything below as 'tab', 'return' or 'control characters'.
135 SkipWhiteSpace( tagBuffer, tagEndBuffer );
137 // Find the attributes.
138 unsigned int attributeIndex = 0u;
139 const char* nameBuffer = NULL;
140 const char* valueBuffer = NULL;
141 Length nameLength = 0u;
142 Length valueLength = 0u;
144 bool addToNameValue = true;
145 Length numberOfWhiteSpace = 0u;
146 for( ; tagBuffer < tagEndBuffer; ++tagBuffer )
148 const char character = *tagBuffer;
149 if( ( WHITE_SPACE >= character ) && !isQuotationOpen )
151 if( NULL != valueBuffer )
153 // Remove white spaces at the end of the value.
154 valueLength -= numberOfWhiteSpace;
157 if( ( NULL != nameBuffer ) && ( NULL != valueBuffer ) )
159 // Every time a white space is found, a new attribute is created and stored in the attributes vector.
160 Attribute& attribute = *( tag.attributes.Begin() + attributeIndex );
163 attribute.nameBuffer = nameBuffer;
164 attribute.valueBuffer = valueBuffer;
165 attribute.nameLength = nameLength;
166 attribute.valueLength = valueLength;
173 addToNameValue = true; // next read characters will be added to the name.
176 else if( EQUAL == character ) // '='
178 addToNameValue = false; // next read characters will be added to the value.
179 SkipWhiteSpace( tagBuffer, tagEndBuffer );
181 else if( QUOTATION_MARK == character ) // '\''
183 // Do not add quotation marks to neither name nor value.
184 isQuotationOpen = !isQuotationOpen;
186 if( isQuotationOpen )
189 SkipWhiteSpace( tagBuffer, tagEndBuffer );
195 // Adds characters to the name or the value.
198 if( NULL == nameBuffer )
200 nameBuffer = tagBuffer;
206 if( isQuotationOpen )
208 if( WHITE_SPACE >= character )
210 ++numberOfWhiteSpace;
214 numberOfWhiteSpace = 0u;
217 if( NULL == valueBuffer )
219 valueBuffer = tagBuffer;
226 if( NULL != valueBuffer )
228 // Remove white spaces at the end of the value.
229 valueLength -= numberOfWhiteSpace;
232 if( ( NULL != nameBuffer ) && ( NULL != valueBuffer ) )
234 // Checks if the last attribute needs to be added.
235 Attribute& attribute = *( tag.attributes.Begin() + attributeIndex );
238 attribute.nameBuffer = nameBuffer;
239 attribute.valueBuffer = valueBuffer;
240 attribute.nameLength = nameLength;
241 attribute.valueLength = valueLength;
244 // Resize the vector of attributes.
245 tag.attributes.Resize( attributeIndex );
249 * @brief It parses a tag and its attributes if the given iterator @e it is pointing at a tag beginning.
251 * @param[in,out] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
252 * @param[in] markupStringEndBuffer Pointer to one character after the end of the mark-up string buffer.
253 * @param[out] tag The tag with its attributes.
255 * @return @e true if the iterator @e it is pointing a mark-up tag. Otherwise @e false.
257 bool IsTag( const char*& markupStringBuffer,
258 const char* const markupStringEndBuffer,
262 bool isQuotationOpen = false;
263 bool attributesFound = false;
264 tag.isEndTag = false;
266 const char character = *markupStringBuffer;
267 if( LESS_THAN == character ) // '<'
272 // if the iterator is pointing to a '<' character, then check if it's a mark-up tag is needed.
273 ++markupStringBuffer;
274 if( markupStringBuffer < markupStringEndBuffer )
276 SkipWhiteSpace( markupStringBuffer, markupStringEndBuffer );
278 for( ; ( !isTag ) && ( markupStringBuffer < markupStringEndBuffer ); ++markupStringBuffer )
280 const char character = *markupStringBuffer;
282 if( SLASH == character ) // '/'
284 // if the tag has a '/' then it's an end or empty tag.
287 if( ( markupStringBuffer + 1u < markupStringEndBuffer ) && ( WHITE_SPACE >= *( markupStringBuffer + 1u ) ) && ( !isQuotationOpen ) )
289 ++markupStringBuffer;
290 SkipWhiteSpace( markupStringBuffer, markupStringEndBuffer );
291 --markupStringBuffer;
294 else if( GREATER_THAN == character ) // '>'
298 else if( QUOTATION_MARK == character )
300 isQuotationOpen = !isQuotationOpen;
303 else if( WHITE_SPACE >= character ) // ' '
305 // If the tag contains white spaces then it may have attributes.
306 if( !isQuotationOpen )
308 attributesFound = true;
314 if( NULL == tag.buffer )
316 tag.buffer = markupStringBuffer;
319 // If it's not any of the 'special' characters then just add it to the tag string.
325 // If the tag string has white spaces, then parse the attributes is needed.
326 if( attributesFound )
328 ParseAttributes( tag );
337 void ProcessMarkupString( const std::string& markupString, MarkupProcessData& markupProcessData )
339 // Reserve space for the plain text.
340 const Length markupStringSize = markupString.size();
341 markupProcessData.markupProcessedText.reserve( markupStringSize );
343 // Stores a struct with the index to the first character of the run, the type of run and its parameters.
344 StyleStack styleStack;
346 // Points the next free position in the vector of runs.
347 StyleStack::RunIndex colorRunIndex = 0u;
348 StyleStack::RunIndex fontRunIndex = 0u;
350 // Give an initial default value to the model's vectors.
351 markupProcessData.colorRuns.Reserve( DEFAULT_VECTOR_SIZE );
352 markupProcessData.fontRuns.Reserve( DEFAULT_VECTOR_SIZE );
354 // Get the mark-up string buffer.
355 const char* markupStringBuffer = markupString.c_str();
356 const char* const markupStringEndBuffer = markupStringBuffer + markupStringSize;
359 CharacterIndex characterIndex = 0u;
360 for( ; markupStringBuffer < markupStringEndBuffer; )
362 if( IsTag( markupStringBuffer,
363 markupStringEndBuffer,
366 if( TokenComparison( XHTML_COLOR_TAG, tag.buffer, tag.length ) )
370 // Create a new color run.
372 colorRun.characterRun.numberOfCharacters = 0u;
374 // Set the start character index.
375 colorRun.characterRun.characterIndex = characterIndex;
377 // Fill the run with the attributes.
378 ProcessColorTag( tag, colorRun );
380 // Push the color run in the logical model.
381 markupProcessData.colorRuns.PushBack( colorRun );
383 // Push the index of the run into the stack.
384 styleStack.Push( colorRunIndex );
386 // Point the next color run.
391 // Pop the top of the stack and set the number of characters of the run.
392 ColorRun& colorRun = *( markupProcessData.colorRuns.Begin() + styleStack.Pop() );
393 colorRun.characterRun.numberOfCharacters = characterIndex - colorRun.characterRun.characterIndex;
396 else if( TokenComparison( XHTML_I_TAG, tag.buffer, tag.length ) )
400 // Create a new font run.
401 FontDescriptionRun fontRun;
402 fontRun.characterRun.numberOfCharacters = 0u;
404 // Fill the run with the parameters.
405 fontRun.characterRun.characterIndex = characterIndex;
406 fontRun.slant = TextAbstraction::FontSlant::ITALIC;
408 fontRun.familyName = NULL;
409 fontRun.familyDefined = false;
410 fontRun.weightDefined = false;
411 fontRun.widthDefined = false;
412 fontRun.slantDefined = true;
413 fontRun.sizeDefined = false;
415 // Push the font run in the logical model.
416 markupProcessData.fontRuns.PushBack( fontRun );
418 // Push the index of the run into the stack.
419 styleStack.Push( fontRunIndex );
421 // Point the next free font run.
426 // Pop the top of the stack and set the number of characters of the run.
427 FontDescriptionRun& fontRun = *( markupProcessData.fontRuns.Begin() + styleStack.Pop() );
428 fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
431 else if( TokenComparison( XHTML_U_TAG, tag.buffer, tag.length ) )
435 // Create a new underline run.
439 // Pop the top of the stack and set the number of characters of the run.
442 else if( TokenComparison( XHTML_B_TAG, tag.buffer, tag.length ) )
446 // Create a new font run.
447 FontDescriptionRun fontRun;
448 fontRun.characterRun.numberOfCharacters = 0u;
450 // Fill the run with the parameters.
451 fontRun.characterRun.characterIndex = characterIndex;
453 fontRun.weight = TextAbstraction::FontWeight::BOLD;
455 fontRun.familyName = NULL;
456 fontRun.familyDefined = false;
457 fontRun.weightDefined = true;
458 fontRun.widthDefined = false;
459 fontRun.slantDefined = false;
460 fontRun.sizeDefined = false;
462 // Push the font run in the logical model.
463 markupProcessData.fontRuns.PushBack( fontRun );
465 // Push the index of the run into the stack.
466 styleStack.Push( fontRunIndex );
468 // Point the next free font run.
473 // Pop the top of the stack and set the number of characters of the run.
474 FontDescriptionRun& fontRun = *( markupProcessData.fontRuns.Begin() + styleStack.Pop() );
475 fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
478 else if( TokenComparison( XHTML_FONT_TAG, tag.buffer, tag.length ) )
482 // Create a new font run.
483 FontDescriptionRun fontRun;
484 fontRun.characterRun.numberOfCharacters = 0u;
486 // Fill the run with the parameters.
487 fontRun.characterRun.characterIndex = characterIndex;
489 fontRun.familyName = NULL;
490 fontRun.familyDefined = false;
491 fontRun.weightDefined = false;
492 fontRun.widthDefined = false;
493 fontRun.slantDefined = false;
494 fontRun.sizeDefined = false;
496 ProcessFontTag( tag, fontRun );
498 // Push the font run in the logical model.
499 markupProcessData.fontRuns.PushBack( fontRun );
501 // Push the index of the run into the stack.
502 styleStack.Push( fontRunIndex );
504 // Point the next free font run.
509 // Pop the top of the stack and set the number of characters of the run.
510 FontDescriptionRun& fontRun = *( markupProcessData.fontRuns.Begin() + styleStack.Pop() );
511 fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
514 else if( TokenComparison( XHTML_SHADOW_TAG, tag.buffer, tag.length ) )
518 // Create a new shadow run.
522 // Pop the top of the stack and set the number of characters of the run.
524 } // <shadow></shadow>
525 else if( TokenComparison( XHTML_GLOW_TAG, tag.buffer, tag.length ) )
529 // Create a new glow run.
533 // Pop the top of the stack and set the number of characters of the run.
536 else if( TokenComparison( XHTML_OUTLINE_TAG, tag.buffer, tag.length ) )
540 // Create a new outline run.
544 // Pop the top of the stack and set the number of characters of the run.
546 } // <outline></outline>
547 } // end if( IsTag() )
550 unsigned char character = *markupStringBuffer;
552 if( ( BACK_SLASH == character ) && ( markupStringBuffer + 1u < markupStringEndBuffer ) )
554 // Adding < or > special character.
555 const unsigned char nextCharacter = *( markupStringBuffer + 1u );
556 if( ( LESS_THAN == nextCharacter ) || ( GREATER_THAN == nextCharacter ) )
558 character = nextCharacter;
559 ++markupStringBuffer;
563 const unsigned char numberOfBytes = GetUtf8Length( character );
565 markupProcessData.markupProcessedText.push_back( character );
566 for( unsigned char i = 1u; i < numberOfBytes; ++i )
568 ++markupStringBuffer;
569 markupProcessData.markupProcessedText.push_back( *markupStringBuffer );
573 ++markupStringBuffer;
577 // Resize the model's vectors.
578 if( 0u == fontRunIndex )
580 markupProcessData.fontRuns.Clear();
584 markupProcessData.fontRuns.Resize( fontRunIndex );
587 if( 0u == colorRunIndex )
589 markupProcessData.colorRuns.Clear();
593 markupProcessData.colorRuns.Resize( colorRunIndex );
599 } // namespace Toolkit