2 * Copyright (c) 2022 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/markup-processor.h>
22 #include <dali/integration-api/debug.h>
23 #include <climits> // for ULONG_MAX
27 #include <dali-toolkit/internal/text/character-set-conversion.h>
28 #include <dali-toolkit/internal/text/markup-processor/markup-processor-anchor.h>
29 #include <dali-toolkit/internal/text/markup-processor/markup-processor-background.h>
30 #include <dali-toolkit/internal/text/markup-processor/markup-processor-character-spacing.h>
31 #include <dali-toolkit/internal/text/markup-processor/markup-processor-color.h>
32 #include <dali-toolkit/internal/text/markup-processor/markup-processor-embedded-item.h>
33 #include <dali-toolkit/internal/text/markup-processor/markup-processor-font.h>
34 #include <dali-toolkit/internal/text/markup-processor/markup-processor-helper-functions.h>
35 #include <dali-toolkit/internal/text/markup-processor/markup-processor-paragraph.h>
36 #include <dali-toolkit/internal/text/markup-processor/markup-processor-span.h>
37 #include <dali-toolkit/internal/text/markup-processor/markup-processor-strikethrough.h>
38 #include <dali-toolkit/internal/text/markup-processor/markup-processor-underline.h>
39 #include <dali-toolkit/internal/text/markup-tags-and-attributes.h>
40 #include <dali-toolkit/internal/text/xhtml-entities.h>
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 = '\\';
56 const char AMPERSAND = '&';
57 const char HASH = '#';
58 const char SEMI_COLON = ';';
59 const char CHAR_ARRAY_END = '\0';
60 const char HEX_CODE = 'x';
62 const char WHITE_SPACE = 0x20; // ASCII value of the white space.
63 const char NEW_LINE = 0x0A; // ASCII value of the newline.
65 // Range 1 0x0u < XHTML_DECIMAL_ENTITY_RANGE <= 0xD7FFu
66 // Range 2 0xE000u < XHTML_DECIMAL_ENTITY_RANGE <= 0xFFFDu
67 // Range 3 0x10000u < XHTML_DECIMAL_ENTITY_RANGE <= 0x10FFFFu
68 const unsigned long XHTML_DECIMAL_ENTITY_RANGE[] = {0x0u, 0xD7FFu, 0xE000u, 0xFFFDu, 0x10000u, 0x10FFFFu};
70 // The MAX_NUM_OF_ATTRIBUTES is the number of attributes in span tag "markup-processor-span.cpp". Because it contains the maximum number of attributes in all tags.
71 const unsigned int MAX_NUM_OF_ATTRIBUTES = 14u; ///< The span tag has the 'font-family', 'font-size' 'font-weight', 'font-width', 'font-slant','text-color', 'u-color', 'u-height','u-type','u-dash-gap', 'u-dash-width', 's-color', 's-height' and 'char-space-value' attrubutes.
72 const unsigned int DEFAULT_VECTOR_SIZE = 16u; ///< Default size of run vectors.
74 #if defined(DEBUG_ENABLED)
75 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_MARKUP_PROCESSOR");
78 typedef VectorBase::SizeType RunIndex;
81 * @brief Struct used to retrieve the style runs from the mark-up string.
83 template<typename StyleStackType>
86 Vector<StyleStackType> stack; ///< Use a vector as a style stack.
87 unsigned int topIndex; ///< Points the top of the stack.
93 stack.Resize(DEFAULT_VECTOR_SIZE);
96 void Push(StyleStackType item)
98 // Check if there is space inside the style stack.
99 const VectorBase::SizeType size = stack.Count();
102 // Resize the style stack.
103 stack.Resize(2u * size);
106 // Set the item in the top of the stack.
107 *(stack.Begin() + topIndex) = item;
109 // Reposition the pointer to the top of the stack.
115 // Pop the top of the stack.
117 return *(stack.Begin() + topIndex);
122 * @brief Struct used to retrieve spans from the mark-up string.
126 RunIndex colorRunIndex;
127 RunIndex fontRunIndex;
128 RunIndex underlinedCharacterRunIndex;
129 RunIndex backgroundColorRunIndex;
130 RunIndex strikethroughCharacterRunIndex;
131 RunIndex characterSpacingCharacterRunIndex;
135 bool isUnderlinedCharacterDefined;
136 bool isBackgroundColorDefined;
137 bool isStrikethroughDefined;
138 bool isCharacterSpacingDefined;
142 * @brief Struct used to retrieve anchors from the mark-up string.
144 struct AnchorForStack
146 RunIndex colorRunIndex;
147 RunIndex underlinedCharacterRunIndex;
151 * @brief Initializes a font run description to its defaults.
153 * @param[in,out] fontRun The font description run to initialize.
155 void Initialize(FontDescriptionRun& fontRun)
157 fontRun.characterRun.characterIndex = 0u;
158 fontRun.characterRun.numberOfCharacters = 0u;
159 fontRun.familyName = NULL;
160 fontRun.familyLength = 0u;
161 fontRun.weight = TextAbstraction::FontWeight::NORMAL;
162 fontRun.width = TextAbstraction::FontWidth::NORMAL;
163 fontRun.slant = TextAbstraction::FontSlant::NORMAL;
165 fontRun.familyDefined = false;
166 fontRun.weightDefined = false;
167 fontRun.widthDefined = false;
168 fontRun.slantDefined = false;
169 fontRun.sizeDefined = false;
173 * @brief Initializes a color run description to its defaults.
175 * @param[in,out] colorRun The font description run to initialize.
177 void Initialize(ColorRun& colorRun)
179 colorRun.characterRun.characterIndex = 0u;
180 colorRun.characterRun.numberOfCharacters = 0u;
184 * @brief Initializes a underlined character run to its defaults.
186 * @param[in,out] underlinedCharacterRun The underelined character run to initialize.
188 void Initialize(UnderlinedCharacterRun& underlinedCharacterRun)
190 underlinedCharacterRun.characterRun.characterIndex = 0u;
191 underlinedCharacterRun.characterRun.numberOfCharacters = 0u;
195 * @brief Initializes a span to its defaults.
197 * @param[in,out] span The span to be initialized.
199 void Initialize(Span& span)
201 span.colorRunIndex = 0u;
202 span.isColorDefined = false;
204 span.fontRunIndex = 0u;
205 span.isFontDefined = false;
207 span.underlinedCharacterRunIndex = 0u;
208 span.isUnderlinedCharacterDefined = false;
209 span.backgroundColorRunIndex = 0u;
210 span.isBackgroundColorDefined = false;
213 span.strikethroughCharacterRunIndex = 0u;
214 span.isStrikethroughDefined = false;
217 span.characterSpacingCharacterRunIndex = 0u;
218 span.isCharacterSpacingDefined = false;
222 * @brief Initializes a anchor to its defaults.
224 * @param[in,out] anchor The anchor to be initialized.
226 void Initialize(AnchorForStack& anchor)
228 anchor.colorRunIndex = 0u;
229 anchor.underlinedCharacterRunIndex = 0u;
233 * @brief Initializes a strikethrough character run to its defaults.
235 * @param[in,out] strikethroughCharacterRun The strikethrough character run to initialize.
237 void Initialize(StrikethroughCharacterRun& strikethroughCharacterRun)
239 strikethroughCharacterRun.characterRun.characterIndex = 0u;
240 strikethroughCharacterRun.characterRun.numberOfCharacters = 0u;
241 strikethroughCharacterRun.properties.colorDefined = false;
245 * @brief Initializes a bounded-paragraph character run to its defaults.
247 * @param[in,out] boundedParagraphRun The bounded paragraphRun run to initialize.
249 void Initialize(BoundedParagraphRun& boundedParagraphRun)
251 boundedParagraphRun.characterRun.characterIndex = 0u;
252 boundedParagraphRun.characterRun.numberOfCharacters = 0u;
256 * @brief Initializes a character-spacing run to its defaults.
258 * @param[in,out] characterSpacingCharacterRun The character-spacing run to initialize.
260 void Initialize(CharacterSpacingCharacterRun& characterSpacingCharacterRun)
262 characterSpacingCharacterRun.characterRun.characterIndex = 0u;
263 characterSpacingCharacterRun.characterRun.numberOfCharacters = 0u;
264 characterSpacingCharacterRun.value = 0.0f;
268 * @brief Splits the tag string into the tag name and its attributes.
270 * The attributes are stored in a vector in the tag.
272 * @param[in,out] tag The tag.
274 void ParseAttributes(Tag& tag)
276 if(tag.buffer == NULL)
281 tag.attributes.Resize(MAX_NUM_OF_ATTRIBUTES);
283 // Find first the tag name.
284 bool isQuotationOpen = false;
286 const char* tagBuffer = tag.buffer;
287 const char* const tagEndBuffer = tagBuffer + tag.length;
289 for(; tagBuffer < tagEndBuffer; ++tagBuffer)
291 const char character = *tagBuffer;
292 if(WHITE_SPACE < character)
298 // Stops counting the length of the tag when a white space is found.
299 // @note a white space is the WHITE_SPACE character and anything below as 'tab', 'return' or 'control characters'.
303 SkipWhiteSpace(tagBuffer, tagEndBuffer);
305 // Find the attributes.
306 unsigned int attributeIndex = 0u;
307 const char* nameBuffer = NULL;
308 const char* valueBuffer = NULL;
309 Length nameLength = 0u;
310 Length valueLength = 0u;
312 bool addToNameValue = true;
313 Length numberOfWhiteSpace = 0u;
314 for(; tagBuffer < tagEndBuffer; ++tagBuffer)
316 const char character = *tagBuffer;
317 if((WHITE_SPACE >= character) && !isQuotationOpen)
319 if(NULL != valueBuffer)
321 // Remove white spaces at the end of the value.
322 valueLength -= numberOfWhiteSpace;
325 if((NULL != nameBuffer) && (NULL != valueBuffer))
327 // Every time a white space is found, a new attribute is created and stored in the attributes vector.
328 Attribute& attribute = *(tag.attributes.Begin() + attributeIndex);
331 attribute.nameBuffer = nameBuffer;
332 attribute.valueBuffer = valueBuffer;
333 attribute.nameLength = nameLength;
334 attribute.valueLength = valueLength;
341 addToNameValue = true; // next read characters will be added to the name.
344 else if(EQUAL == character) // '='
346 addToNameValue = false; // next read characters will be added to the value.
347 SkipWhiteSpace(tagBuffer, tagEndBuffer);
349 else if(QUOTATION_MARK == character) // '\''
351 // Do not add quotation marks to neither name nor value.
352 isQuotationOpen = !isQuotationOpen;
357 SkipWhiteSpace(tagBuffer, tagEndBuffer);
363 // Adds characters to the name or the value.
366 if(NULL == nameBuffer)
368 nameBuffer = tagBuffer;
376 if(WHITE_SPACE >= character)
378 ++numberOfWhiteSpace;
382 numberOfWhiteSpace = 0u;
385 if(NULL == valueBuffer)
387 valueBuffer = tagBuffer;
394 if(NULL != valueBuffer)
396 // Remove white spaces at the end of the value.
397 valueLength -= numberOfWhiteSpace;
400 if((NULL != nameBuffer) && (NULL != valueBuffer))
402 // Checks if the last attribute needs to be added.
403 Attribute& attribute = *(tag.attributes.Begin() + attributeIndex);
406 attribute.nameBuffer = nameBuffer;
407 attribute.valueBuffer = valueBuffer;
408 attribute.nameLength = nameLength;
409 attribute.valueLength = valueLength;
412 // Resize the vector of attributes.
413 tag.attributes.Resize(attributeIndex);
417 * @brief It parses a tag and its attributes if the given iterator @e it is pointing at a tag beginning.
419 * @param[in,out] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
420 * @param[in] markupStringEndBuffer Pointer to one character after the end of the mark-up string buffer.
421 * @param[out] tag The tag with its attributes.
423 * @return @e true if the iterator @e it is pointing a mark-up tag. Otherwise @e false.
425 bool IsTag(const char*& markupStringBuffer,
426 const char* const markupStringEndBuffer,
430 bool isQuotationOpen = false;
431 bool attributesFound = false;
432 tag.isEndTag = false;
433 bool isPreviousLessThan = false;
434 bool isPreviousSlash = false;
436 const char character = *markupStringBuffer;
437 if(LESS_THAN == character) // '<'
441 isPreviousLessThan = true;
443 // if the iterator is pointing to a '<' character, then check if it's a mark-up tag is needed.
444 ++markupStringBuffer;
445 if(markupStringBuffer < markupStringEndBuffer)
447 SkipWhiteSpace(markupStringBuffer, markupStringEndBuffer);
449 for(; (!isTag) && (markupStringBuffer < markupStringEndBuffer); ++markupStringBuffer)
451 const char character = *markupStringBuffer;
453 if(!isQuotationOpen && (SLASH == character)) // '/'
455 if(isPreviousLessThan)
461 // if the tag has a '/' it may be an end tag.
462 isPreviousSlash = true;
465 isPreviousLessThan = false;
466 if((markupStringBuffer + 1u < markupStringEndBuffer) && (WHITE_SPACE >= *(markupStringBuffer + 1u)))
468 ++markupStringBuffer;
469 SkipWhiteSpace(markupStringBuffer, markupStringEndBuffer);
470 --markupStringBuffer;
473 else if(GREATER_THAN == character) // '>'
481 isPreviousSlash = false;
482 isPreviousLessThan = false;
484 else if(QUOTATION_MARK == character)
486 isQuotationOpen = !isQuotationOpen;
489 isPreviousSlash = false;
490 isPreviousLessThan = false;
492 else if(WHITE_SPACE >= character) // ' '
494 // If the tag contains white spaces then it may have attributes.
497 attributesFound = true;
503 if(NULL == tag.buffer)
505 tag.buffer = markupStringBuffer;
508 // If it's not any of the 'special' characters then just add it to the tag string.
511 isPreviousSlash = false;
512 isPreviousLessThan = false;
517 // If the tag string has white spaces, then parse the attributes is needed.
520 ParseAttributes(tag);
528 * @brief Returns length of XHTML entity by parsing the text. It also determines if it is XHTML entity or not.
530 * @param[in] markupStringBuffer The mark-up string buffer. It's a const iterator pointing the current character.
531 * @param[in] markupStringEndBuffer Pointing to end of mark-up string buffer.
533 * @return Length of markupText in case of XHTML entity otherwise return 0.
535 unsigned int GetXHTMLEntityLength(const char*& markupStringBuffer,
536 const char* const markupStringEndBuffer)
538 char character = *markupStringBuffer;
539 if(AMPERSAND == character) // '&'
541 // if the iterator is pointing to a '&' character, then check for ';' to find end to XHTML entity.
542 ++markupStringBuffer;
543 if(markupStringBuffer < markupStringEndBuffer)
545 unsigned int len = 1u;
546 for(; markupStringBuffer < markupStringEndBuffer; ++markupStringBuffer)
548 character = *markupStringBuffer;
550 if(SEMI_COLON == character) // ';'
552 // found end of XHTML entity
553 ++markupStringBuffer;
556 else if((AMPERSAND == character) || (BACK_SLASH == character) || (LESS_THAN == character))
567 * @brief It parses a XHTML string which has hex/decimal entity and fill its corresponging utf-8 string.
569 * @param[in] markupText The mark-up text buffer.
570 * @param[out] utf-8 text Corresponding to markup Text
572 * @return true if string is successfully parsed otherwise false
574 bool XHTMLNumericEntityToUtf8(const char* markupText, char* utf8)
578 if(NULL != markupText)
582 // check if hex or decimal entity
583 if((CHAR_ARRAY_END != *markupText) && (HEX_CODE == *markupText))
590 unsigned long l = strtoul(markupText, &end, (isHex ? 16 : 10)); // l contains UTF-32 code in case of correct XHTML entity
592 // check for valid XHTML numeric entities (between '#' or "#x" and ';')
593 if((l > 0) && (l < ULONG_MAX) && (*end == SEMI_COLON)) // in case wrong XHTML entity is set eg. "abcdefs;" in that case *end will be 'a'
595 /* characters XML 1.1 permits */
596 if(((XHTML_DECIMAL_ENTITY_RANGE[0] < l) && (l <= XHTML_DECIMAL_ENTITY_RANGE[1])) ||
597 ((XHTML_DECIMAL_ENTITY_RANGE[2] <= l) && (l <= XHTML_DECIMAL_ENTITY_RANGE[3])) ||
598 ((XHTML_DECIMAL_ENTITY_RANGE[4] <= l) && (l <= XHTML_DECIMAL_ENTITY_RANGE[5])))
600 // Convert UTF32 code to UTF8
601 Utf32ToUtf8(reinterpret_cast<const uint32_t* const>(&l), 1, reinterpret_cast<uint8_t*>(utf8));
610 * @brief Processes a particular tag for the required run (color-run, font-run or underlined-character-run).
612 * @tparam RunType Whether ColorRun , FontDescriptionRun or UnderlinedCharacterRun
614 * @param[in/out] runsContainer The container containing all the runs
615 * @param[in/out] styleStack The style stack
616 * @param[in] tag The tag we are currently processing
617 * @param[in] characterIndex The current character index
618 * @param[in/out] runIndex The run index
619 * @param[in/out] tagReference The tagReference we should increment/decrement
620 * @param[in] parameterSettingFunction This function will be called to set run specific parameters
622 template<typename RunType>
623 void ProcessTagForRun(
624 Vector<RunType>& runsContainer,
625 StyleStack<RunIndex>& styleStack,
627 const CharacterIndex characterIndex,
630 std::function<void(const Tag&, RunType&)> parameterSettingFunction)
638 // Fill the run with the parameters.
639 run.characterRun.characterIndex = characterIndex;
640 parameterSettingFunction(tag, run);
642 // Push the run in the logical model.
643 runsContainer.PushBack(run);
645 // Push the index of the run into the stack.
646 styleStack.Push(runIndex);
648 // Point the next free run.
651 // Increase reference
658 // Pop the top of the stack and set the number of characters of the run.
659 RunType& run = *(runsContainer.Begin() + styleStack.Pop());
660 run.characterRun.numberOfCharacters = characterIndex - run.characterRun.characterIndex;
667 * @brief Processes the item tag
669 * @param[in/out] markupProcessData The markup process data
670 * @param[in] tag The current tag
671 * @param[in/out] characterIndex The current character index
674 MarkupProcessData& markupProcessData,
676 CharacterIndex& characterIndex)
680 // Create an embedded item instance.
682 item.characterIndex = characterIndex;
683 ProcessEmbeddedItem(tag, item);
685 markupProcessData.items.PushBack(item);
687 // Insert white space character that will be replaced by the item.
688 markupProcessData.markupProcessedText.append(1u, WHITE_SPACE);
694 * @brief Processes the paragraph-tag
696 * @param[in/out] markupProcessData The markup process data
697 * @param[in] tag The current tag
698 * @param[in] isEndBuffer Whether the end of buffer
699 * @param[in/out] characterIndex The current character index
701 void ProcessParagraphTag(
702 MarkupProcessData& markupProcessData,
705 CharacterIndex& characterIndex)
707 if((characterIndex > 0 &&
708 markupProcessData.markupProcessedText[characterIndex - 1u] != NEW_LINE) &&
709 (!(tag.isEndTag && isEndBuffer)))
711 // Insert new-line character at the start and end of paragraph.
712 markupProcessData.markupProcessedText.append(1u, NEW_LINE);
718 * @brief Processes span tag for the color-run & font-run.
720 * @param[in] spanTag The tag we are currently processing
721 * @param[inout] spanStack The spans stack
722 * @param[inout] colorRuns The container containing all the color runs
723 * @param[inout] fontRuns The container containing all the font description runs
724 * @param[inout] underlinedCharacterRuns The container containing all the underlined character runs
725 * @param[inout] strikethroughCharacterRuns The container containing all the strikethroughed character runs
726 * @param[inout] colorRunIndex The color run index
727 * @param[inout] fontRunIndex The font run index
728 * @param[inout] underlinedCharacterRunIndex The underlined character run index
729 * @param[inout] strikethroughCharacterRunIndex The strikethroughed character run index
730 * @param[in] characterIndex The current character index
731 * @param[in] tagReference The tagReference we should increment/decrement
733 void ProcessSpanForRun(
735 StyleStack<Span>& spanStack,
736 Vector<ColorRun>& colorRuns,
737 Vector<FontDescriptionRun>& fontRuns,
738 Vector<UnderlinedCharacterRun>& underlinedCharacterRuns,
739 Vector<ColorRun>& backgroundColorRuns,
740 Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns,
741 Vector<CharacterSpacingCharacterRun>& characterSpacingCharacterRuns,
742 RunIndex& colorRunIndex,
743 RunIndex& fontRunIndex,
744 RunIndex& underlinedCharacterRunIndex,
745 RunIndex& backgroundColorRunIndex,
746 RunIndex& strikethroughCharacterRunIndex,
747 RunIndex& characterSpacingCharacterRunIndex,
748 const CharacterIndex characterIndex,
751 if(!spanTag.isEndTag)
755 Initialize(colorRun);
757 FontDescriptionRun fontRun;
760 UnderlinedCharacterRun underlinedCharacterRun;
761 Initialize(underlinedCharacterRun);
763 ColorRun backgroundColorRun;
764 Initialize(backgroundColorRun);
766 StrikethroughCharacterRun strikethroughCharacterRun;
767 Initialize(strikethroughCharacterRun);
769 CharacterSpacingCharacterRun characterSpacingCharacterRun;
770 Initialize(characterSpacingCharacterRun);
775 // Fill the run with the parameters.
776 colorRun.characterRun.characterIndex = characterIndex;
777 fontRun.characterRun.characterIndex = characterIndex;
778 underlinedCharacterRun.characterRun.characterIndex = characterIndex;
779 backgroundColorRun.characterRun.characterIndex = characterIndex;
780 strikethroughCharacterRun.characterRun.characterIndex = characterIndex;
781 characterSpacingCharacterRun.characterRun.characterIndex = characterIndex;
783 span.colorRunIndex = colorRunIndex;
784 span.fontRunIndex = fontRunIndex;
785 span.underlinedCharacterRunIndex = underlinedCharacterRunIndex;
786 span.backgroundColorRunIndex = backgroundColorRunIndex;
787 span.strikethroughCharacterRunIndex = strikethroughCharacterRunIndex;
788 span.characterSpacingCharacterRunIndex = characterSpacingCharacterRunIndex;
790 ProcessSpanTag(spanTag,
793 underlinedCharacterRun,
795 strikethroughCharacterRun,
796 characterSpacingCharacterRun,
799 span.isUnderlinedCharacterDefined,
800 span.isBackgroundColorDefined,
801 span.isStrikethroughDefined,
802 span.isCharacterSpacingDefined);
804 // Push the span into the stack.
805 spanStack.Push(span);
807 // Point the next free run.
808 if(span.isColorDefined)
810 // Push the run in the logical model.
811 colorRuns.PushBack(colorRun);
815 if(span.isFontDefined)
817 // Push the run in the logical model.
818 fontRuns.PushBack(fontRun);
822 if(span.isUnderlinedCharacterDefined)
824 // Push the run in the logical model.
825 underlinedCharacterRuns.PushBack(underlinedCharacterRun);
826 ++underlinedCharacterRunIndex;
829 if(span.isBackgroundColorDefined)
831 // Push the run in the logical model.
832 backgroundColorRuns.PushBack(backgroundColorRun);
833 ++backgroundColorRunIndex;
836 if(span.isStrikethroughDefined)
838 // Push the run in the logical model.
839 strikethroughCharacterRuns.PushBack(strikethroughCharacterRun);
840 ++strikethroughCharacterRunIndex;
843 if(span.isCharacterSpacingDefined)
845 // Push the run in the logical model.
846 characterSpacingCharacterRuns.PushBack(characterSpacingCharacterRun);
847 ++characterSpacingCharacterRunIndex;
850 // Increase reference
857 // Pop the top of the stack and set the number of characters of the run.
858 Span span = spanStack.Pop();
860 if(span.isColorDefined)
862 ColorRun& colorRun = *(colorRuns.Begin() + span.colorRunIndex);
863 colorRun.characterRun.numberOfCharacters = characterIndex - colorRun.characterRun.characterIndex;
866 if(span.isFontDefined)
868 FontDescriptionRun& fontRun = *(fontRuns.Begin() + span.fontRunIndex);
869 fontRun.characterRun.numberOfCharacters = characterIndex - fontRun.characterRun.characterIndex;
872 if(span.isUnderlinedCharacterDefined)
874 UnderlinedCharacterRun& underlinedCharacterRun = *(underlinedCharacterRuns.Begin() + span.underlinedCharacterRunIndex);
875 underlinedCharacterRun.characterRun.numberOfCharacters = characterIndex - underlinedCharacterRun.characterRun.characterIndex;
878 if(span.isBackgroundColorDefined)
880 ColorRun& backgroundColorRun = *(backgroundColorRuns.Begin() + span.backgroundColorRunIndex);
881 backgroundColorRun.characterRun.numberOfCharacters = characterIndex - backgroundColorRun.characterRun.characterIndex;
884 if(span.isStrikethroughDefined)
886 StrikethroughCharacterRun& strikethroughCharacterRun = *(strikethroughCharacterRuns.Begin() + span.strikethroughCharacterRunIndex);
887 strikethroughCharacterRun.characterRun.numberOfCharacters = characterIndex - strikethroughCharacterRun.characterRun.characterIndex;
890 if(span.isCharacterSpacingDefined)
892 CharacterSpacingCharacterRun& characterSpacingCharacterRun = *(characterSpacingCharacterRuns.Begin() + span.characterSpacingCharacterRunIndex);
893 characterSpacingCharacterRun.characterRun.numberOfCharacters = characterIndex - characterSpacingCharacterRun.characterRun.characterIndex;
902 * @brief Processes anchor tag for the color-run & underline-run.
904 * @param[in,out] markupProcessData The markup process data
905 * @param[in] tag The tag we are currently processing
906 * @param[in,out] anchorStack The anchors stack
907 * @param[in,out] colorRuns The container containing all the color runs
908 * @param[in,out] underlinedCharacterRuns The container containing all the underlined character runs
909 * @param[in,out] colorRunIndex The color run index
910 * @param[in,out] underlinedCharacterRunIndex The underlined character run index
911 * @param[in] characterIndex The current character index
912 * @param[in] tagReference The tagReference we should increment/decrement
914 void ProcessAnchorForRun(
915 MarkupProcessData& markupProcessData,
917 StyleStack<AnchorForStack>& anchorStack,
918 Vector<ColorRun>& colorRuns,
919 Vector<UnderlinedCharacterRun>& underlinedCharacterRuns,
920 RunIndex& colorRunIndex,
921 RunIndex& underlinedCharacterRunIndex,
922 const CharacterIndex characterIndex,
927 // Create an anchor instance.
929 anchor.href = nullptr;
930 anchor.startIndex = characterIndex;
931 anchor.endIndex = 0u;
932 anchor.colorRunIndex = colorRunIndex;
933 anchor.underlinedCharacterRunIndex = underlinedCharacterRunIndex;
937 Initialize(colorRun);
939 UnderlinedCharacterRun underlinedCharacterRun;
940 Initialize(underlinedCharacterRun);
942 AnchorForStack anchorForStack;
943 Initialize(anchorForStack);
945 // Fill the run with the parameters.
946 colorRun.characterRun.characterIndex = characterIndex;
947 underlinedCharacterRun.characterRun.characterIndex = characterIndex;
949 anchorForStack.colorRunIndex = colorRunIndex;
950 anchorForStack.underlinedCharacterRunIndex = underlinedCharacterRunIndex;
952 // Init default color
953 colorRun.color = Color::MEDIUM_BLUE;
954 underlinedCharacterRun.properties.color = Color::MEDIUM_BLUE;
955 underlinedCharacterRun.properties.colorDefined = true;
957 ProcessAnchorTag(tag, anchor, colorRun, underlinedCharacterRun);
959 markupProcessData.anchors.PushBack(anchor);
961 // Push the anchor into the stack.
962 anchorStack.Push(anchorForStack);
964 // Push the run in the logical model.
965 colorRuns.PushBack(colorRun);
968 // Push the run in the logical model.
969 underlinedCharacterRuns.PushBack(underlinedCharacterRun);
970 ++underlinedCharacterRunIndex;
972 // Increase reference
980 unsigned int count = markupProcessData.anchors.Count();
983 markupProcessData.anchors[count - 1].endIndex = characterIndex;
986 // Pop the top of the stack and set the number of characters of the run.
987 AnchorForStack anchorForStack = anchorStack.Pop();
989 ColorRun& colorRun = *(colorRuns.Begin() + anchorForStack.colorRunIndex);
990 colorRun.characterRun.numberOfCharacters = characterIndex - colorRun.characterRun.characterIndex;
992 UnderlinedCharacterRun& underlinedCharacterRun = *(underlinedCharacterRuns.Begin() + anchorForStack.underlinedCharacterRunIndex);
993 underlinedCharacterRun.characterRun.numberOfCharacters = characterIndex - underlinedCharacterRun.characterRun.characterIndex;
1001 * @brief Resizes the model's vectors
1003 * @param[inout] markupProcessData The markup process data
1004 * @param[in] fontRunIndex The font run index
1005 * @param[in] colorRunIndex The color run index
1006 * @param[in] underlinedCharacterRunIndex The underlined character run index
1007 * @param[in] strikethroughCharacterRunIndex The strikethroughed character run index
1008 * @param[in] backgroundRunIndex The background run index
1009 * @param[in] boundedParagraphRunIndex The bounded paragraph run index
1010 * @param[in] characterSpacingCharacterRunIndex The character-spacing character run index
1013 void ResizeModelVectors(MarkupProcessData& markupProcessData,
1014 const RunIndex fontRunIndex,
1015 const RunIndex colorRunIndex,
1016 const RunIndex underlinedCharacterRunIndex,
1017 const RunIndex strikethroughCharacterRunIndex,
1018 const RunIndex backgroundRunIndex,
1019 const RunIndex boundedParagraphRunIndex,
1020 const RunIndex characterSpacingCharacterRunIndex)
1022 markupProcessData.fontRuns.Resize(fontRunIndex);
1023 markupProcessData.colorRuns.Resize(colorRunIndex);
1024 markupProcessData.underlinedCharacterRuns.Resize(underlinedCharacterRunIndex);
1025 markupProcessData.strikethroughCharacterRuns.Resize(strikethroughCharacterRunIndex);
1026 markupProcessData.backgroundColorRuns.Resize(backgroundRunIndex);
1027 markupProcessData.boundedParagraphRuns.Resize(boundedParagraphRunIndex);
1028 markupProcessData.characterSpacingCharacterRuns.Resize(characterSpacingCharacterRunIndex);
1030 #ifdef DEBUG_ENABLED
1031 if(gLogFilter->IsEnabledFor(Debug::Verbose))
1033 for(uint32_t i = 0; i < colorRunIndex; ++i)
1035 ColorRun& run = markupProcessData.colorRuns[i];
1036 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "run[%d] index: %d, length: %d, color %f,%f,%f,%f\n", i, run.characterRun.characterIndex, run.characterRun.numberOfCharacters, run.color.r, run.color.g, run.color.b, run.color.a);
1043 * @brief Processes the markup string buffer
1045 * @param[in/out] markupProcessData The markup process data
1046 * @param[in/out] markupStringBuffer The markup string buffer pointer
1047 * @param[in] markupStringEndBuffer The markup string end buffer pointer
1048 * @param[in/out] characterIndex The current character index
1050 void ProcessMarkupStringBuffer(
1051 MarkupProcessData& markupProcessData,
1052 const char*& markupStringBuffer,
1053 const char* const markupStringEndBuffer,
1054 CharacterIndex& characterIndex)
1056 unsigned char character = *markupStringBuffer;
1057 const char* markupBuffer = markupStringBuffer;
1058 unsigned char count = GetUtf8Length(character);
1061 if((BACK_SLASH == character) && (markupStringBuffer + 1u < markupStringEndBuffer))
1063 // Adding < , > or & special character.
1064 const unsigned char nextCharacter = *(markupStringBuffer + 1u);
1065 if((LESS_THAN == nextCharacter) || (GREATER_THAN == nextCharacter) || (AMPERSAND == nextCharacter))
1067 character = nextCharacter;
1068 ++markupStringBuffer;
1070 count = GetUtf8Length(character);
1071 markupBuffer = markupStringBuffer;
1074 else // checking if contains XHTML entity or not
1076 const unsigned int len = GetXHTMLEntityLength(markupStringBuffer, markupStringEndBuffer);
1078 // Parse markupStringTxt if it contains XHTML Entity between '&' and ';'
1081 char* entityCode = NULL;
1082 bool result = false;
1085 // Checking if XHTML Numeric Entity
1086 if(HASH == *(markupBuffer + 1u))
1088 entityCode = &utf8[0];
1089 // markupBuffer is currently pointing to '&'. By adding 2u to markupBuffer it will point to numeric string by skipping "&#'
1090 result = XHTMLNumericEntityToUtf8((markupBuffer + 2u), entityCode);
1092 else // Checking if XHTML Named Entity
1094 entityCode = const_cast<char*>(NamedEntityToUtf8(markupBuffer, len));
1095 result = (entityCode != NULL);
1099 markupBuffer = entityCode; //utf8 text assigned to markupBuffer
1100 character = markupBuffer[0];
1104 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Not valid XHTML entity : (%.*s) \n", len, markupBuffer);
1105 markupBuffer = NULL;
1108 else // in case string conatins Start of XHTML Entity('&') but not its end character(';')
1110 if(character == AMPERSAND)
1112 markupBuffer = NULL;
1113 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Not Well formed XHTML content \n");
1118 if(markupBuffer != NULL)
1120 const unsigned char numberOfBytes = GetUtf8Length(character);
1121 markupProcessData.markupProcessedText.push_back(character);
1123 for(unsigned char i = 1u; i < numberOfBytes; ++i)
1126 markupProcessData.markupProcessedText.push_back(*markupBuffer);
1130 markupStringBuffer += count;
1136 void ProcessMarkupString(const std::string& markupString, MarkupProcessData& markupProcessData)
1138 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "markupString: %s\n", markupString.c_str());
1140 // Reserve space for the plain text.
1141 const Length markupStringSize = markupString.size();
1142 markupProcessData.markupProcessedText.reserve(markupStringSize);
1144 // Stores a struct with the index to the first character of the run, the type of run and its parameters.
1145 StyleStack<RunIndex> styleStack;
1147 // Stores a struct with the index to the first character of the color run & color font for the span.
1148 StyleStack<Span> spanStack;
1150 StyleStack<AnchorForStack> anchorStack;
1152 // Points the next free position in the vector of runs.
1153 RunIndex colorRunIndex = 0u;
1154 RunIndex fontRunIndex = 0u;
1155 RunIndex underlinedCharacterRunIndex = 0u;
1156 RunIndex backgroundRunIndex = 0u;
1157 RunIndex strikethroughCharacterRunIndex = 0u;
1158 RunIndex boundedParagraphRunIndex = 0u;
1159 RunIndex characterSpacingCharacterRunIndex = 0u;
1161 // check tag reference
1162 int colorTagReference = 0u;
1163 int fontTagReference = 0u;
1164 int iTagReference = 0u;
1165 int bTagReference = 0u;
1166 int uTagReference = 0u;
1167 int backgroundTagReference = 0u;
1168 int spanTagReference = 0u;
1169 int sTagReference = 0u;
1170 int pTagReference = 0u;
1171 int characterSpacingTagReference = 0u;
1172 int aTagReference = 0u;
1174 // Give an initial default value to the model's vectors.
1175 markupProcessData.colorRuns.Reserve(DEFAULT_VECTOR_SIZE);
1176 markupProcessData.fontRuns.Reserve(DEFAULT_VECTOR_SIZE);
1177 markupProcessData.underlinedCharacterRuns.Reserve(DEFAULT_VECTOR_SIZE);
1178 markupProcessData.backgroundColorRuns.Reserve(DEFAULT_VECTOR_SIZE);
1179 markupProcessData.strikethroughCharacterRuns.Reserve(DEFAULT_VECTOR_SIZE);
1180 markupProcessData.characterSpacingCharacterRuns.Reserve(DEFAULT_VECTOR_SIZE);
1182 // Get the mark-up string buffer.
1183 const char* markupStringBuffer = markupString.c_str();
1184 const char* const markupStringEndBuffer = markupStringBuffer + markupStringSize;
1187 CharacterIndex characterIndex = 0u;
1188 for(; markupStringBuffer < markupStringEndBuffer;)
1190 tag.attributes.Clear();
1191 if(IsTag(markupStringBuffer,
1192 markupStringEndBuffer,
1195 if(TokenComparison(MARKUP::TAG::COLOR, tag.buffer, tag.length))
1197 ProcessTagForRun<ColorRun>(
1198 markupProcessData.colorRuns, styleStack, tag, characterIndex, colorRunIndex, colorTagReference, [](const Tag& tag, ColorRun& run) { ProcessColorTag(tag, run); });
1199 } // <color></color>
1200 else if(TokenComparison(MARKUP::TAG::ITALIC, tag.buffer, tag.length))
1202 ProcessTagForRun<FontDescriptionRun>(
1203 markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, iTagReference, [](const Tag&, FontDescriptionRun& fontRun) {
1204 fontRun.slant = TextAbstraction::FontSlant::ITALIC;
1205 fontRun.slantDefined = true;
1208 else if(TokenComparison(MARKUP::TAG::UNDERLINE, tag.buffer, tag.length))
1210 ProcessTagForRun<UnderlinedCharacterRun>(
1211 markupProcessData.underlinedCharacterRuns, styleStack, tag, characterIndex, underlinedCharacterRunIndex, uTagReference, [](const Tag& tag, UnderlinedCharacterRun& run) { ProcessUnderlineTag(tag, run); });
1213 else if(TokenComparison(MARKUP::TAG::BOLD, tag.buffer, tag.length))
1215 ProcessTagForRun<FontDescriptionRun>(
1216 markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, bTagReference, [](const Tag&, FontDescriptionRun& fontRun) {
1217 fontRun.weight = TextAbstraction::FontWeight::BOLD;
1218 fontRun.weightDefined = true;
1221 else if(TokenComparison(MARKUP::TAG::FONT, tag.buffer, tag.length))
1223 ProcessTagForRun<FontDescriptionRun>(
1224 markupProcessData.fontRuns, styleStack, tag, characterIndex, fontRunIndex, fontTagReference, [](const Tag& tag, FontDescriptionRun& fontRun) { ProcessFontTag(tag, fontRun); });
1226 else if(TokenComparison(MARKUP::TAG::ANCHOR, tag.buffer, tag.length))
1228 ProcessAnchorForRun(markupProcessData,
1231 markupProcessData.colorRuns,
1232 markupProcessData.underlinedCharacterRuns,
1234 underlinedCharacterRunIndex,
1237 } // <a href=https://www.tizen.org>tizen</a>
1238 else if(TokenComparison(MARKUP::TAG::SHADOW, tag.buffer, tag.length))
1240 // TODO: If !tag.isEndTag, then create a new shadow run.
1241 // else Pop the top of the stack and set the number of characters of the run.
1242 } // <shadow></shadow>
1243 else if(TokenComparison(MARKUP::TAG::GLOW, tag.buffer, tag.length))
1245 // TODO: If !tag.isEndTag, then create a new glow run.
1246 // else Pop the top of the stack and set the number of characters of the run.
1248 else if(TokenComparison(MARKUP::TAG::OUTLINE, tag.buffer, tag.length))
1250 // TODO: If !tag.isEndTag, then create a new outline run.
1251 // else Pop the top of the stack and set the number of characters of the run.
1252 } // <outline></outline>
1253 else if(TokenComparison(MARKUP::TAG::EMBEDDED_ITEM, tag.buffer, tag.length))
1255 ProcessItemTag(markupProcessData, tag, characterIndex);
1257 else if(TokenComparison(MARKUP::TAG::BACKGROUND, tag.buffer, tag.length))
1259 ProcessTagForRun<ColorRun>(
1260 markupProcessData.backgroundColorRuns, styleStack, tag, characterIndex, backgroundRunIndex, backgroundTagReference, [](const Tag& tag, ColorRun& run) { ProcessBackground(tag, run); });
1262 else if(TokenComparison(MARKUP::TAG::SPAN, tag.buffer, tag.length))
1264 ProcessSpanForRun(tag,
1266 markupProcessData.colorRuns,
1267 markupProcessData.fontRuns,
1268 markupProcessData.underlinedCharacterRuns,
1269 markupProcessData.backgroundColorRuns,
1270 markupProcessData.strikethroughCharacterRuns,
1271 markupProcessData.characterSpacingCharacterRuns,
1274 underlinedCharacterRunIndex,
1276 strikethroughCharacterRunIndex,
1277 characterSpacingCharacterRunIndex,
1281 else if(TokenComparison(MARKUP::TAG::STRIKETHROUGH, tag.buffer, tag.length))
1283 ProcessTagForRun<StrikethroughCharacterRun>(
1284 markupProcessData.strikethroughCharacterRuns, styleStack, tag, characterIndex, strikethroughCharacterRunIndex, sTagReference, [](const Tag& tag, StrikethroughCharacterRun& run) { ProcessStrikethroughTag(tag, run); });
1286 else if(TokenComparison(MARKUP::TAG::PARAGRAPH, tag.buffer, tag.length))
1288 ProcessParagraphTag(markupProcessData, tag, (markupStringBuffer == markupStringEndBuffer), characterIndex);
1289 ProcessTagForRun<BoundedParagraphRun>(
1290 markupProcessData.boundedParagraphRuns, styleStack, tag, characterIndex, boundedParagraphRunIndex, pTagReference, [](const Tag& tag, BoundedParagraphRun& run) { ProcessAttributesOfParagraphTag(tag, run); });
1292 else if(TokenComparison(MARKUP::TAG::CHARACTER_SPACING, tag.buffer, tag.length))
1294 ProcessTagForRun<CharacterSpacingCharacterRun>(
1295 markupProcessData.characterSpacingCharacterRuns, styleStack, tag, characterIndex, characterSpacingCharacterRunIndex, characterSpacingTagReference, [](const Tag& tag, CharacterSpacingCharacterRun& run) { ProcessCharacterSpacingTag(tag, run); });
1296 } // <char-spacing></char-spacing>
1297 } // end if( IsTag() )
1298 else if(markupStringBuffer < markupStringEndBuffer)
1300 ProcessMarkupStringBuffer(markupProcessData, markupStringBuffer, markupStringEndBuffer, characterIndex);
1304 // Resize the model's vectors.
1305 ResizeModelVectors(markupProcessData, fontRunIndex, colorRunIndex, underlinedCharacterRunIndex, strikethroughCharacterRunIndex, backgroundRunIndex, boundedParagraphRunIndex, characterSpacingCharacterRunIndex);
1307 // Handle the nested tags
1308 OverrideNestedUnderlinedCharacterRuns(markupProcessData.underlinedCharacterRuns);
1309 OverrideNestedStrikethroughCharacterRuns(markupProcessData.strikethroughCharacterRuns);
1314 } // namespace Toolkit