Support Underline to Markup using underlined-character-run 71/256771/9
authorShrouq Sabah <s.sabah@samsung.com>
Mon, 12 Apr 2021 13:01:32 +0000 (16:01 +0300)
committerShrouq Sabah <s.sabah@samsung.com>
Mon, 26 Apr 2021 06:54:48 +0000 (09:54 +0300)
Change-Id: I21e5639a75a815204060e5d56c342794e23941ff

27 files changed:
automated-tests/src/dali-toolkit-internal/CMakeLists.txt
automated-tests/src/dali-toolkit-internal/dali-toolkit-test-utils/toolkit-text-utils.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-Text-Markup.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextEditor-internal.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextField-internal.cpp
automated-tests/src/dali-toolkit-internal/utc-Dali-TextLabel-internal.cpp [new file with mode: 0755]
dali-toolkit/devel-api/text/text-utils-devel.cpp
dali-toolkit/internal/text/logical-model-impl.cpp
dali-toolkit/internal/text/logical-model-impl.h
dali-toolkit/internal/text/markup-processor.cpp
dali-toolkit/internal/text/markup-processor.h
dali-toolkit/internal/text/rendering/atlas/text-atlas-renderer.cpp
dali-toolkit/internal/text/rendering/text-typesetter.cpp
dali-toolkit/internal/text/rendering/text-typesetter.h
dali-toolkit/internal/text/rendering/view-model.cpp
dali-toolkit/internal/text/rendering/view-model.h
dali-toolkit/internal/text/text-controller-impl.cpp
dali-toolkit/internal/text/text-controller-impl.h
dali-toolkit/internal/text/text-controller-text-updater.cpp
dali-toolkit/internal/text/text-controller.cpp
dali-toolkit/internal/text/text-model-interface.h
dali-toolkit/internal/text/text-model.cpp
dali-toolkit/internal/text/text-model.h
dali-toolkit/internal/text/underlined-character-run.h [new file with mode: 0644]
dali-toolkit/internal/text/visual-model-impl.cpp
dali-toolkit/internal/text/visual-model-impl.h
dali-toolkit/internal/visuals/text/text-visual.cpp

index cc18fe6..1162266 100755 (executable)
@@ -30,6 +30,7 @@ SET(TC_SOURCES
  utc-Dali-Text-ViewModel.cpp
  utc-Dali-TextField-internal.cpp
  utc-Dali-TextEditor-internal.cpp
+ utc-Dali-TextLabel-internal.cpp
  utc-Dali-TextSelectionPopup-internal.cpp
  utc-Dali-TextureManager.cpp
  utc-Dali-Visuals-internal.cpp
index d98f397..1e745b1 100755 (executable)
@@ -109,7 +109,8 @@ void CreateTextModel( const std::string& text,
   MarkupProcessData markupProcessData( logicalModel->mColorRuns,
                                        logicalModel->mFontDescriptionRuns,
                                        logicalModel->mEmbeddedItems,
-                                       logicalModel->mAnchors );
+                                       logicalModel->mAnchors,
+                                       logicalModel->mUnderlinedCharacterRuns);
 
   Length textSize = 0u;
   const uint8_t* utf8 = NULL;
index 6e2d632..f46c401 100755 (executable)
@@ -185,7 +185,8 @@ namespace
     Vector<FontDescriptionRun> fontRuns;
     Vector<EmbeddedItem> items;
     Vector<Anchor> anchors;
-    MarkupProcessData markupProcessData( colorRuns, fontRuns, items, anchors );
+    Vector<UnderlinedCharacterRun> underlinedCharacterRuns;
+    MarkupProcessData markupProcessData( colorRuns, fontRuns, items, anchors, underlinedCharacterRuns );
     ProcessMarkupString( data.xHTMLEntityString, markupProcessData );
 
     for( Vector<EmbeddedItem>::Iterator it = items.Begin(),
index 9445be6..ca724ff 100755 (executable)
@@ -70,3 +70,42 @@ int UtcDaliTextEditorSelectText(void)
 
   END_TEST;
 }
+
+int UtcDaliTextEditorMarkupUnderline(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorMarkupUnderline ");
+
+  TextEditor textEditor = TextEditor::New();
+
+  application.GetScene().Add( textEditor );
+
+  textEditor.SetProperty( TextEditor::Property::TEXT, "<u>ABC</u>EF<u>GH</u>" );
+  textEditor.SetProperty( TextEditor ::Property::ENABLE_MARKUP,  true );
+
+  application.SendNotification();
+  application.Render();
+
+  uint32_t expectedNumberOfUnderlinedGlyphs = 5u;
+
+  Toolkit::Internal::TextEditor& textEditorImpl = GetImpl( textEditor );
+  const Text::Length numberOfUnderlineRuns = textEditorImpl.getController()->GetTextModel()->GetNumberOfUnderlineRuns();
+
+  DALI_TEST_EQUALS( numberOfUnderlineRuns, expectedNumberOfUnderlinedGlyphs, TEST_LOCATION );
+
+  Vector<GlyphRun> underlineRuns;
+  underlineRuns.Resize(numberOfUnderlineRuns);
+  textEditorImpl.getController()->GetTextModel()->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
+
+  //ABC are underlined
+  DALI_TEST_EQUALS( underlineRuns[0u].glyphIndex, 0u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[1u].glyphIndex, 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[2u].glyphIndex, 2u, TEST_LOCATION);
+
+  //GH are underlined
+  DALI_TEST_EQUALS( underlineRuns[3u].glyphIndex, 5u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[4u].glyphIndex, 6u, TEST_LOCATION);
+
+  END_TEST;
+
+}
\ No newline at end of file
index 37d54e1..1d899d9 100755 (executable)
@@ -152,3 +152,42 @@ int UtcDaliTextFieldSelectText(void)
 
   END_TEST;
 }
+
+int UtcDaliTextFieldMarkupUnderline(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextFieldMarkupUnderline ");
+
+  TextField textField = TextField::New();
+
+  application.GetScene().Add( textField );
+
+  textField.SetProperty( TextField::Property::TEXT, "<u>ABC</u>EF<u>GH</u>" );
+  textField.SetProperty( TextField ::Property::ENABLE_MARKUP,  true );
+
+  application.SendNotification();
+  application.Render();
+
+  uint32_t expectedNumberOfUnderlinedGlyphs = 5u;
+
+  Toolkit::Internal::TextField& textFieldImpl = GetImpl( textField );
+  const Text::Length numberOfUnderlineRuns = textFieldImpl.getController()->GetTextModel()->GetNumberOfUnderlineRuns();
+
+  DALI_TEST_EQUALS( numberOfUnderlineRuns, expectedNumberOfUnderlinedGlyphs, TEST_LOCATION );
+
+  Vector<GlyphRun> underlineRuns;
+  underlineRuns.Resize(numberOfUnderlineRuns);
+  textFieldImpl.getController()->GetTextModel()->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
+
+  //ABC are underlined
+  DALI_TEST_EQUALS( underlineRuns[0u].glyphIndex, 0u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[1u].glyphIndex, 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[2u].glyphIndex, 2u, TEST_LOCATION);
+
+  //GH are underlined
+  DALI_TEST_EQUALS( underlineRuns[3u].glyphIndex, 5u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[4u].glyphIndex, 6u, TEST_LOCATION);
+
+  END_TEST;
+
+}
\ No newline at end of file
diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-TextLabel-internal.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-TextLabel-internal.cpp
new file mode 100755 (executable)
index 0000000..e85ea21
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <iostream>
+#include <stdlib.h>
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+
+#include <dali-toolkit/internal/controls/text-controls/text-label-impl.h>
+#include <dali-toolkit/internal/text/text-controller.h>
+#include <dali-toolkit/internal/text/text-controller-impl.h>
+
+using namespace Dali;
+using namespace Toolkit;
+using namespace Text;
+
+int UtcDaliTextLabelMarkupUnderline(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextLabelMarkupUnderline ");
+
+  TextLabel textLabel = TextLabel::New();
+
+  application.GetScene().Add( textLabel );
+
+  textLabel.SetProperty( TextLabel::Property::TEXT, "<u>ABC</u>EF<u>GH</u>" );
+  textLabel.SetProperty( TextLabel ::Property::ENABLE_MARKUP,  true );
+
+  application.SendNotification();
+  application.Render();
+
+  uint32_t expectedNumberOfUnderlinedGlyphs = 5u;
+
+  Toolkit::Internal::TextLabel& textLabelImpl = GetImpl( textLabel );
+  const Text::Length numberOfUnderlineRuns = textLabelImpl.getController()->GetTextModel()->GetNumberOfUnderlineRuns();
+
+  DALI_TEST_EQUALS( numberOfUnderlineRuns, expectedNumberOfUnderlinedGlyphs, TEST_LOCATION );
+
+  Vector<GlyphRun> underlineRuns;
+  underlineRuns.Resize(numberOfUnderlineRuns);
+  textLabelImpl.getController()->GetTextModel()->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
+
+  //ABC are underlined
+  DALI_TEST_EQUALS( underlineRuns[0u].glyphIndex, 0u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[1u].glyphIndex, 1u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[2u].glyphIndex, 2u, TEST_LOCATION);
+
+  //GH are underlined
+  DALI_TEST_EQUALS( underlineRuns[3u].glyphIndex, 5u, TEST_LOCATION);
+  DALI_TEST_EQUALS( underlineRuns[4u].glyphIndex, 6u, TEST_LOCATION);
+
+  END_TEST;
+
+}
\ No newline at end of file
index 1441dc4..787cc0f 100644 (file)
@@ -173,7 +173,8 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
   MarkupProcessData markupProcessData(colorRuns,
                                       fontDescriptionRuns,
                                       textModel->mLogicalModel->mEmbeddedItems,
-                                      textModel->mLogicalModel->mAnchors);
+                                      textModel->mLogicalModel->mAnchors,
+                                      textModel->mLogicalModel->mUnderlinedCharacterRuns);
 
   if(textParameters.markupEnabled)
   {
index 12929fc..0de63d5 100644 (file)
@@ -296,6 +296,15 @@ void LogicalModel::UpdateTextStyleRuns(CharacterIndex index, int numberOfCharact
                                 mColorRuns,
                                 removedColorRuns);
 
+  // This is needed until now for underline tag in mark-up processor
+  // Process the underlined runs.
+  Vector<UnderlinedCharacterRun> removedUnderlinedCharacterRuns;
+  UpdateCharacterRuns<UnderlinedCharacterRun>(index,
+                                numberOfCharacters,
+                                totalNumberOfCharacters,
+                                mUnderlinedCharacterRuns,
+                                removedUnderlinedCharacterRuns);
+
   // Process the background color runs.
   Vector<ColorRun> removedBackgroundColorRuns;
   UpdateCharacterRuns<ColorRun>(index,
index 921d911..80a1285 100644 (file)
@@ -33,6 +33,7 @@
 #include <dali-toolkit/internal/text/font-run.h>
 #include <dali-toolkit/internal/text/paragraph-run.h>
 #include <dali-toolkit/internal/text/script-run.h>
+#include <dali-toolkit/internal/text/underlined-character-run.h>
 
 namespace Dali
 {
@@ -219,6 +220,7 @@ public:
   Vector<BidirectionalLineInfoRun>      mBidirectionalLineInfo;
   Vector<EmbeddedItem>                  mEmbeddedItems;
   Vector<Anchor>                        mAnchors;
+  Vector<UnderlinedCharacterRun>        mUnderlinedCharacterRuns; ///< The underlined character run from markup-processor
 
   BidirectionalLineRunIndex mBidirectionalLineIndex; ///< The last fetched bidirectional line info.
 };
index a8a6ecc..ea7f869 100644 (file)
@@ -156,6 +156,17 @@ void Initialize(ColorRun& colorRun)
 }
 
 /**
+ * @brief Initializes a underlined character run to its defaults.
+ *
+ * @param[in,out] underlinedCharacterRun The underelined character run to initialize.
+ */
+void Initialize(UnderlinedCharacterRun& underlinedCharacterRun)
+{
+  underlinedCharacterRun.characterRun.characterIndex     = 0u;
+  underlinedCharacterRun.characterRun.numberOfCharacters = 0u;
+}
+
+/**
  * @brief Splits the tag string into the tag name and its attributes.
  *
  * The attributes are stored in a vector in the tag.
@@ -498,9 +509,9 @@ bool XHTMLNumericEntityToUtf8(const char* markupText, char* utf8)
 }
 
 /**
- * @brief Processes a particular tag for the required run (color-run or font-run).
+ * @brief Processes a particular tag for the required run (color-run, font-run or underlined-character-run).
  *
- * @tparam RunType Whether ColorRun or FontDescriptionRun
+ * @tparam RunType Whether ColorRun , FontDescriptionRun or UnderlinedCharacterRun
  *
  * @param[in/out] runsContainer The container containing all the runs
  * @param[in/out] styleStack The style stack
@@ -619,11 +630,13 @@ void ProcessAnchorTag(
  * @param[in/out] markupProcessData The markup process data
  * @param[in] fontRunIndex The font run index
  * @param[in] colorRunIndex The color run index
+ * @param[in] underlinedCharacterRunIndex The underlined character run index
  */
-void ResizeModelVectors(MarkupProcessData& markupProcessData, const StyleStack::RunIndex fontRunIndex, const StyleStack::RunIndex colorRunIndex)
+void ResizeModelVectors(MarkupProcessData& markupProcessData, const StyleStack::RunIndex fontRunIndex, const StyleStack::RunIndex colorRunIndex, const StyleStack::RunIndex underlinedCharacterRunIndex)
 {
   markupProcessData.fontRuns.Resize(fontRunIndex);
   markupProcessData.colorRuns.Resize(colorRunIndex);
+  markupProcessData.underlinedCharacterRuns.Resize(underlinedCharacterRunIndex);
 
 #ifdef DEBUG_ENABLED
   for(unsigned int i = 0; i < colorRunIndex; ++i)
@@ -740,18 +753,21 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
   StyleStack styleStack;
 
   // Points the next free position in the vector of runs.
-  StyleStack::RunIndex colorRunIndex = 0u;
-  StyleStack::RunIndex fontRunIndex  = 0u;
+  StyleStack::RunIndex colorRunIndex                = 0u;
+  StyleStack::RunIndex fontRunIndex                 = 0u;
+  StyleStack::RunIndex underlinedCharacterRunIndex  = 0u;
 
   // check tag reference
   int colorTagReference = 0u;
   int fontTagReference  = 0u;
   int iTagReference     = 0u;
   int bTagReference     = 0u;
+  int uTagReference     = 0u;
 
   // Give an initial default value to the model's vectors.
   markupProcessData.colorRuns.Reserve(DEFAULT_VECTOR_SIZE);
   markupProcessData.fontRuns.Reserve(DEFAULT_VECTOR_SIZE);
+  markupProcessData.underlinedCharacterRuns.Reserve(DEFAULT_VECTOR_SIZE);
 
   // Get the mark-up string buffer.
   const char*       markupStringBuffer    = markupString.c_str();
@@ -781,9 +797,9 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
       } // <i></i>
       else if(TokenComparison(XHTML_U_TAG, tag.buffer, tag.length))
       {
-        // TODO: If !tag.isEndTag, then create a new underline run.
-        //       else Pop the top of the stack and set the number of characters of the run.
-      } // <u></u>
+        ProcessTagForRun<UnderlinedCharacterRun>(
+          markupProcessData.underlinedCharacterRuns, styleStack, tag, characterIndex, underlinedCharacterRunIndex, uTagReference, [](const Tag& tag, UnderlinedCharacterRun& run) {  });
+      }  // <u></u>
       else if(TokenComparison(XHTML_B_TAG, tag.buffer, tag.length))
       {
         ProcessTagForRun<FontDescriptionRun>(
@@ -836,7 +852,7 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
   }
 
   // Resize the model's vectors.
-  ResizeModelVectors(markupProcessData, fontRunIndex, colorRunIndex);
+  ResizeModelVectors(markupProcessData, fontRunIndex, colorRunIndex, underlinedCharacterRunIndex);
 }
 
 } // namespace Text
index c75ca0c..ec45957 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali-toolkit/internal/text/color-run.h>
 #include <dali-toolkit/internal/text/embedded-item.h>
 #include <dali-toolkit/internal/text/font-description-run.h>
+#include <dali-toolkit/internal/text/underlined-character-run.h>
 
 namespace Dali
 {
@@ -42,20 +43,23 @@ struct MarkupProcessData
   MarkupProcessData(Vector<ColorRun>&           colorRuns,
                     Vector<FontDescriptionRun>& fontRuns,
                     Vector<EmbeddedItem>&       items,
-                    Vector<Anchor>&             anchors)
+                    Vector<Anchor>&             anchors,
+                    Vector<UnderlinedCharacterRun>& underlinedCharacterRuns)
   : colorRuns(colorRuns),
     fontRuns(fontRuns),
     items(items),
     anchors(anchors),
+    underlinedCharacterRuns(underlinedCharacterRuns),
     markupProcessedText()
   {
   }
 
-  Vector<ColorRun>&           colorRuns;           ///< The color runs.
-  Vector<FontDescriptionRun>& fontRuns;            ///< The font description runs.
-  Vector<EmbeddedItem>&       items;               ///< The embedded items.
-  Vector<Anchor>&             anchors;             ///< The anchors.
-  std::string                 markupProcessedText; ///< The mark-up string.
+  Vector<ColorRun>&               colorRuns;               ///< The color runs.
+  Vector<FontDescriptionRun>&     fontRuns;                ///< The font description runs.
+  Vector<EmbeddedItem>&           items;                   ///< The embedded items.
+  Vector<Anchor>&                 anchors;                 ///< The anchors.
+  Vector<UnderlinedCharacterRun>& underlinedCharacterRuns; ///< The underlined character runs.
+  std::string                     markupProcessedText;     ///< The mark-up string.
 };
 
 /**
index 208460c..7b51d5b 100644 (file)
@@ -85,7 +85,8 @@ struct AtlasRenderer::Impl
       mRight(0.0f),
       mUnderlinePosition(0.0f),
       mUnderlineThickness(0.0f),
-      mMeshRecordIndex(0u)
+      mMeshRecordIndex(0u),
+      mUnderlineChunkId(0u)
     {
     }
 
@@ -95,6 +96,7 @@ struct AtlasRenderer::Impl
     float    mUnderlinePosition;
     float    mUnderlineThickness;
     uint32_t mMeshRecordIndex;
+    uint32_t mUnderlineChunkId;
   };
 
   struct MaxBlockSize
@@ -288,7 +290,8 @@ struct AtlasRenderer::Impl
                     float                    currentUnderlineThickness,
                     std::vector<MeshRecord>& meshContainer,
                     Vector<TextCacheEntry>&  newTextCache,
-                    Vector<Extent>&          extents)
+                    Vector<Extent>&          extents,
+                    uint32_t                 underlineChunkId)
   {
     // Generate mesh data for this quad, plugging in our supplied position
     AtlasManager::Mesh2D newMesh;
@@ -324,7 +327,8 @@ struct AtlasRenderer::Impl
                    underlineGlyph,
                    currentUnderlinePosition,
                    currentUnderlineThickness,
-                   slot);
+                   slot,
+                   underlineChunkId);
   }
 
   void CreateActors(const std::vector<MeshRecord>& meshContainer,
@@ -458,6 +462,10 @@ struct AtlasRenderer::Impl
     const Vector2* const   positionsBuffer = positions.Begin();
     const Vector2          lineOffsetPosition(minLineOffset, 0.f);
 
+    //For septated underlined chunks. (this is for Markup case)
+    uint32_t underlineChunkId = 0u; // give id for each chunk.
+    bool     isPreUnderlined = false; // status of underlined for previous glyph.
+
     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
     {
       const GlyphInfo& glyph             = *(glyphsBuffer + i);
@@ -504,6 +512,7 @@ struct AtlasRenderer::Impl
           }
 
           lastUnderlinedFontId = glyph.fontId;
+
         } // underline
 
         AtlasGlyphManager::GlyphStyle style;
@@ -548,7 +557,8 @@ struct AtlasRenderer::Impl
                        currentUnderlineThickness,
                        meshContainer,
                        newTextCache,
-                       extents);
+                       extents,
+                       underlineChunkId);
 
           lastFontId = glyph.fontId; // Prevents searching for existing blocksizes when string of the same fontId.
         }
@@ -565,8 +575,19 @@ struct AtlasRenderer::Impl
                        currentUnderlineThickness,
                        meshContainerOutline,
                        newTextCache,
-                       extents);
+                       extents,
+                       0u);
+        }
+
+
+        //The new underlined chunk. Add new id if they are not consecutive indices (this is for Markup case)
+        // Examples: "Hello <u>World</u> Hello <u>World</u>", "<u>World</u> Hello <u>World</u>", "<u>   World</u> Hello <u>World</u>"
+        if( isPreUnderlined && (isPreUnderlined != isGlyphUnderlined))
+        {
+          underlineChunkId++;
         }
+        //Keep status of underlined for previous glyph to check consecutive indices
+        isPreUnderlined = isGlyphUnderlined;
       }
     } // glyphs
 
@@ -717,7 +738,8 @@ struct AtlasRenderer::Impl
                       bool                     underlineGlyph,
                       float                    underlinePosition,
                       float                    underlineThickness,
-                      AtlasManager::AtlasSlot& slot)
+                      AtlasManager::AtlasSlot& slot,
+                      uint32_t                 underlineChunkId)
   {
     if(slot.mImageId)
     {
@@ -745,7 +767,8 @@ struct AtlasRenderer::Impl
                           right,
                           baseLine,
                           underlinePosition,
-                          underlineThickness);
+                          underlineThickness,
+                          underlineChunkId);
           }
 
           return;
@@ -768,7 +791,8 @@ struct AtlasRenderer::Impl
                       right,
                       baseLine,
                       underlinePosition,
-                      underlineThickness);
+                      underlineThickness,
+                      underlineChunkId);
       }
     }
   }
@@ -780,7 +804,8 @@ struct AtlasRenderer::Impl
                      float                    right,
                      float                    baseLine,
                      float                    underlinePosition,
-                     float                    underlineThickness)
+                     float                    underlineThickness,
+                     uint32_t                 underlineChunkId)
   {
     bool foundExtent = false;
     for(Vector<Extent>::Iterator eIt    = extents.Begin(),
@@ -788,7 +813,7 @@ struct AtlasRenderer::Impl
         eIt != eEndIt;
         ++eIt)
     {
-      if(Equals(baseLine, eIt->mBaseLine))
+      if(Equals(baseLine, eIt->mBaseLine) && underlineChunkId == eIt->mUnderlineChunkId)
       {
         foundExtent = true;
         if(left < eIt->mLeft)
@@ -819,6 +844,7 @@ struct AtlasRenderer::Impl
       extent.mUnderlinePosition  = underlinePosition;
       extent.mUnderlineThickness = underlineThickness;
       extent.mMeshRecordIndex    = index;
+      extent.mUnderlineChunkId = underlineChunkId;
       extents.PushBack(extent);
     }
   }
index abf7325..60888db 100644 (file)
@@ -595,6 +595,11 @@ PixelData Typesetter::Render(const Vector2& size, Toolkit::DevelText::TextDirect
       // Combine the two buffers
       imageBuffer = CombineImageBuffer(imageBuffer, backgroundImageBuffer, bufferWidth, bufferHeight);
     }
+
+    // Markup-Processor
+
+    imageBuffer = ApplyMarkupProcessorOnPixelBuffer(imageBuffer, bufferWidth, bufferHeight, ignoreHorizontalAlignment, pixelFormat, penX, penY);
+
   }
 
   // Create the final PixelData for the combined image buffer
@@ -906,6 +911,48 @@ Devel::PixelBuffer Typesetter::CombineImageBuffer(Devel::PixelBuffer topPixelBuf
   return combinedPixelBuffer;
 }
 
+Devel::PixelBuffer Typesetter::ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset)
+{
+    // Apply the markup-Processor if enabled
+    const bool markupProcessorEnabled = mModel->IsMarkupProcessorEnabled();
+    if(markupProcessorEnabled)
+    {
+      // Underline-tags (this is for Markup case)
+      // Get the underline runs.
+      const Length     numberOfUnderlineRuns = mModel->GetNumberOfUnderlineRuns();
+      Vector<GlyphRun> underlineRuns;
+      underlineRuns.Resize(numberOfUnderlineRuns);
+      mModel->GetUnderlineRuns(underlineRuns.Begin(), 0u, numberOfUnderlineRuns);
+
+      // Iterate on the consecutive underlined glyph run and connect them into one chunk of underlined characters.
+      Vector<GlyphRun>::ConstIterator itGlyphRun       = underlineRuns.Begin();
+      Vector<GlyphRun>::ConstIterator endItGlyphRun    = underlineRuns.End();
+      GlyphIndex startGlyphIndex, endGlyphIndex;
+
+      //The outer loop to iterate on the separated chunks of underlined glyph runs
+      while(itGlyphRun != endItGlyphRun)
+      {
+        startGlyphIndex = itGlyphRun->glyphIndex;
+        endGlyphIndex = startGlyphIndex;
+        //The inner loop to make a connected underline for the consecutive characters
+        do
+        {
+          endGlyphIndex += itGlyphRun->numberOfGlyphs;
+          itGlyphRun++;
+        } while(itGlyphRun != endItGlyphRun && itGlyphRun->glyphIndex == endGlyphIndex);
+
+        endGlyphIndex--;
+
+        // Create the image buffer for underline
+        Devel::PixelBuffer underlineImageBuffer = CreateImageBuffer(bufferWidth, bufferHeight, Typesetter::STYLE_UNDERLINE, ignoreHorizontalAlignment, pixelFormat, horizontalOffset, verticalOffset, startGlyphIndex, endGlyphIndex);
+        // Combine the two buffers
+        topPixelBuffer = CombineImageBuffer(topPixelBuffer, underlineImageBuffer, bufferWidth, bufferHeight);
+      }
+    }
+
+    return topPixelBuffer;
+}
+
 Typesetter::Typesetter(const ModelInterface* const model)
 : mModel(new ViewModel(model))
 {
index 2f29a47..c9cd4cc 100644 (file)
@@ -163,6 +163,27 @@ private:
    */
   Devel::PixelBuffer CombineImageBuffer(Devel::PixelBuffer topPixelBuffer, Devel::PixelBuffer bottomPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeightbool);
 
+  /**
+   * @brief Apply behaviour of tags if the markup-processor is enabled.
+   *
+   * The properties on TextLabel override the behavior of Markup.
+   * Because the markup will be the bottom layer buffer
+   *  - i.e: If you set property UNDERLINE to enabled and blue.
+   *    And the TEXT is "<color value='green'>Hello</color> <u>World</u> <i>Hello</i> <b>World</b>".
+   *    Then the output of the whole text is underlined by blue line.
+   *
+   * @param[in] topPixelBuffer The top layer buffer.
+   * @param[in] bufferWidth The width of the image buffer.
+   * @param[in] bufferHeight The height of the image buffer.
+   * @param[in] ignoreHorizontalAlignment Whether to ignore the horizontal alignment, not ignored by default.
+   * @param[in] pixelFormat The format of the pixel in the image that the text is rendered as (i.e. either Pixel::BGRA8888 or Pixel::L8).
+   * @param[in] horizontalOffset The horizontal offset to be added to the glyph's position.
+   * @param[in] verticalOffset The vertical offset to be added to the glyph's position.
+   *
+   * @return The image buffer with the markup.
+   */
+  Devel::PixelBuffer ApplyMarkupProcessorOnPixelBuffer(Devel::PixelBuffer topPixelBuffer, const unsigned int bufferWidth, const unsigned int bufferHeight, bool ignoreHorizontalAlignment, Pixel::Format pixelFormat, int horizontalOffset, int verticalOffset);
+
 protected:
   /**
    * @brief A reference counted object may only be deleted by calling Unreference().
index e7b40a5..2ad03c5 100644 (file)
@@ -225,6 +225,11 @@ bool ViewModel::IsBackgroundEnabled() const
   return mModel->IsBackgroundEnabled();
 }
 
+bool ViewModel::IsMarkupProcessorEnabled() const
+{
+  return mModel->IsMarkupProcessorEnabled();
+}
+
 void ViewModel::ElideGlyphs()
 {
   mIsTextElided = false;
index a84810a..1ea38ee 100644 (file)
@@ -211,6 +211,11 @@ public:
   bool IsBackgroundEnabled() const override;
 
   /**
+   * @copydoc ModelInterface::IsMarkupProcessorEnabled()
+   */
+  bool IsMarkupProcessorEnabled() const override;
+
+  /**
    * @brief Does the text elide.
    *
    * It stores a copy of the visible glyphs and removes as many glyphs as needed
index ec00903..8140c84 100644 (file)
@@ -587,7 +587,7 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
   Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
   Vector<Character>  displayCharacters;
   bool               useHiddenText = false;
-  if(mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText)
+  if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
   {
     mHiddenInput->Substitute(srcCharacters, displayCharacters);
     useHiddenText = true;
@@ -697,7 +697,7 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
       TextAbstraction::FontDescription defaultFontDescription;
       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
 
-      if(IsShowingPlaceholderText() && mEventData && (NULL != mEventData->mPlaceholderFont))
+      if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
       {
         // If the placeholder font is set specifically, only placeholder font is changed.
         defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
@@ -706,7 +706,7 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
           defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * 64u;
         }
       }
-      else if(NULL != mFontDefaults)
+      else if(nullptr != mFontDefaults)
       {
         // Set the normal font and the placeholder font.
         defaultFontDescription = mFontDefaults->mFontDescription;
@@ -807,7 +807,8 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
     mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
     mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
-    updated = true;
+
+   updated = true;
   }
 
   const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
@@ -830,7 +831,7 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
     updated = true;
   }
 
-  if((NULL != mEventData) &&
+  if((nullptr != mEventData) &&
      mEventData->mPreEditFlag &&
      (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
   {
@@ -858,6 +859,12 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
           underlineRun.numberOfGlyphs = numberOfIndices;
           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
+
+          //Mark-up processor case
+          if(mModel->mVisualModel->IsMarkupProcessorEnabled())
+          {
+            CopyUnderlinedFromLogicalToVisualModels(false);
+          }
           break;
         }
         case Dali::InputMethodContext::PreeditStyle::REVERSE:
@@ -902,6 +909,12 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
           underlineRun.numberOfGlyphs = numberOfIndices;
           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
+
+          //Mark-up processor case
+          if(mModel->mVisualModel->IsMarkupProcessorEnabled())
+          {
+            CopyUnderlinedFromLogicalToVisualModels(false);
+          }
           break;
         }
         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
@@ -917,6 +930,12 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
           underlineRun.numberOfGlyphs = numberOfIndices;
           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
+
+          //Mark-up processor case
+          if(mModel->mVisualModel->IsMarkupProcessorEnabled())
+          {
+            CopyUnderlinedFromLogicalToVisualModels(false);
+          }
           break;
         }
         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
@@ -932,6 +951,12 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
           underlineRun.numberOfGlyphs = numberOfIndices;
           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
+
+          //Mark-up processor case
+          if(mModel->mVisualModel->IsMarkupProcessorEnabled())
+          {
+            CopyUnderlinedFromLogicalToVisualModels(false);
+          }
           break;
         }
         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
@@ -947,6 +972,12 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
           underlineRun.glyphIndex     = attrData.startIndex + numberOfCommit;
           underlineRun.numberOfGlyphs = numberOfIndices;
           mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
+
+          //Mark-up processor case
+          if(mModel->mVisualModel->IsMarkupProcessorEnabled())
+          {
+            CopyUnderlinedFromLogicalToVisualModels(false);
+          }
           break;
         }
         case Dali::InputMethodContext::PreeditStyle::NONE:
@@ -985,6 +1016,21 @@ bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
     updated = true;
   }
 
+  if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
+      ! ((nullptr != mEventData) &&
+         mEventData->mPreEditFlag &&
+         (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
+  {
+    //Mark-up processor case
+    if(mModel->mVisualModel->IsMarkupProcessorEnabled())
+    {
+      CopyUnderlinedFromLogicalToVisualModels(true);
+    }
+
+    updated = true;
+  }
+
+
   // The estimated number of lines. Used to avoid reallocations when layouting.
   mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
 
@@ -1064,7 +1110,7 @@ void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
 float Controller::Impl::GetDefaultFontLineHeight()
 {
   FontId defaultFontId = 0u;
-  if(NULL == mFontDefaults)
+  if(nullptr == mFontDefaults)
   {
     TextAbstraction::FontDescription fontDescription;
     defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
@@ -1384,7 +1430,7 @@ void Controller::Impl::SetPopupButtons()
 
 void Controller::Impl::ChangeState(EventData::State newState)
 {
-  if(NULL == mEventData)
+  if(nullptr == mEventData)
   {
     // Nothing to do if there is no text input.
     return;
@@ -1700,7 +1746,7 @@ void Controller::Impl::GetCursorPosition(CharacterIndex logical,
 
 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
 {
-  if(NULL == mEventData)
+  if(nullptr == mEventData)
   {
     // Nothing to do if there is no text input.
     return 0u;
@@ -1750,7 +1796,7 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) c
 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
 {
   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
-  if(NULL == mEventData)
+  if(nullptr == mEventData)
   {
     // Nothing to do if there is no text input.
     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
@@ -1925,7 +1971,7 @@ void Controller::Impl::ScrollTextToMatchCursor()
 
 void Controller::Impl::RequestRelayout()
 {
-  if(NULL != mControlInterface)
+  if(nullptr != mControlInterface)
   {
     mControlInterface->RequestTextRelayout();
   }
@@ -2095,6 +2141,32 @@ Actor Controller::Impl::CreateBackgroundActor()
   return actor;
 }
 
+void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
+{
+    //Underlined character runs for markup-processor
+    const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
+    const Vector<GlyphIndex>&             charactersToGlyph       = mModel->mVisualModel->mCharactersToGlyph;
+    const Vector<Length>&                 glyphsPerCharacter      = mModel->mVisualModel->mGlyphsPerCharacter;
+
+    if(shouldClearPreUnderlineRuns)
+    {
+        mModel->mVisualModel->mUnderlineRuns.Clear();
+    }
+
+    for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
+    {
+        CharacterIndex characterIndex = it->characterRun.characterIndex;
+        Length numberOfCharacters = it->characterRun.numberOfCharacters;
+        for(Length index=0u; index<numberOfCharacters; index++)
+        {
+          GlyphRun underlineGlyphRun;
+          underlineGlyphRun.glyphIndex     = charactersToGlyph[characterIndex + index];
+          underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
+          mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
+        }
+    }
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index ef4bb69..9f637d3 100644 (file)
@@ -785,6 +785,13 @@ private:
   // Declared private and left undefined to avoid copies.
   Impl& operator=(const Impl&);
 
+  /**
+   * @brief Copy Underlined-Character-Runs from Logical-Model to Underlined-Glyph-Runs in Visual-Model
+   *
+   * @param shouldClearPreUnderlineRuns Whether should clear the existing Underlined-Glyph-Runs in Visual-Model
+   */
+  void CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns);
+
 public:
   ControlInterface*           mControlInterface;           ///< Reference to the text controller.
   EditableControlInterface*   mEditableControlInterface;   ///< Reference to the editable text controller.
index 81adb8a..36a2133 100644 (file)
@@ -82,7 +82,8 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string&
     MarkupProcessData markupProcessData(logicalModel->mColorRuns,
                                         logicalModel->mFontDescriptionRuns,
                                         logicalModel->mEmbeddedItems,
-                                        logicalModel->mAnchors);
+                                        logicalModel->mAnchors,
+                                        logicalModel->mUnderlinedCharacterRuns);
 
     Length         textSize = 0u;
     const uint8_t* utf8     = NULL;
index 15db50b..99c0264 100644 (file)
@@ -124,6 +124,8 @@ void Controller::SetMarkupProcessorEnabled(bool enable)
     GetText(text);
     SetText(text);
   }
+
+  mImpl->mModel->mVisualModel->SetMarkupProcessorEnabled(enable);
 }
 
 bool Controller::IsMarkupProcessorEnabled() const
index aafd430..5c3fa1f 100644 (file)
@@ -265,6 +265,13 @@ public:
    * @return The background state.
    */
   virtual bool IsBackgroundEnabled() const = 0;
+
+  /**
+   * @brief Returns whether markup-processor is enabled or not.
+   *
+   * @return The markup-processor state.
+   */
+  virtual bool IsMarkupProcessorEnabled() const = 0;
 };
 
 } // namespace Text
index 2d60854..42a5795 100644 (file)
@@ -184,6 +184,11 @@ bool Model::IsBackgroundEnabled() const
   return mVisualModel->IsBackgroundEnabled();
 }
 
+bool Model::IsMarkupProcessorEnabled() const
+{
+  return mVisualModel->IsMarkupProcessorEnabled();
+}
+
 Model::Model()
 : mLogicalModel(),
   mVisualModel(),
index d52809c..705adeb 100644 (file)
@@ -207,6 +207,11 @@ public:
    */
   bool IsBackgroundEnabled() const override;
 
+  /**
+   * @copydoc ModelInterface::IsMarkupProcessorEnabled()
+   */
+  bool IsMarkupProcessorEnabled() const override;
+
 private: // Private contructors & copy operator.
   /**
    * @brief Private constructor.
diff --git a/dali-toolkit/internal/text/underlined-character-run.h b/dali-toolkit/internal/text/underlined-character-run.h
new file mode 100644 (file)
index 0000000..99d5967
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef DALI_TOOLKIT_TEXT_UNDERLINED_CHARACTER_RUN_H
+#define DALI_TOOLKIT_TEXT_UNDERLINED_CHARACTER_RUN_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/math/vector4.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/character-run.h>
+#include <dali-toolkit/internal/text/glyph-run.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+/**
+ * @brief Run of underlined characters with same properties.
+ */
+struct UnderlinedCharacterRun
+{
+  CharacterRun characterRun; ///< The initial character index and the number of characters of the run.
+  //TODO: add properties like color, height and style
+  //Vector4      color;       ///< The color of underline.
+  //float        height;      ///< The height of underline.
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_UNDERLINED_CHARACTER_RUN_H
index eda5633..7edb428 100644 (file)
@@ -384,6 +384,11 @@ void VisualModel::SetBackgroundEnabled(bool enabled)
   mBackgroundEnabled = enabled;
 }
 
+void VisualModel::SetMarkupProcessorEnabled(bool enabled)
+{
+  mMarkupProcessorEnabled = enabled;
+}
+
 const Vector4& VisualModel::GetTextColor() const
 {
   return mTextColor;
@@ -439,6 +444,11 @@ bool VisualModel::IsBackgroundEnabled() const
   return mBackgroundEnabled;
 }
 
+bool VisualModel::IsMarkupProcessorEnabled() const
+{
+  return mMarkupProcessorEnabled;
+}
+
 Length VisualModel::GetNumberOfUnderlineRuns() const
 {
   return mUnderlineRuns.Count();
@@ -476,7 +486,8 @@ VisualModel::VisualModel()
   mCachedLineIndex(0u),
   mUnderlineEnabled(false),
   mUnderlineColorSet(false),
-  mBackgroundEnabled(false)
+  mBackgroundEnabled(false),
+  mMarkupProcessorEnabled(false)
 {
 }
 
index 371b666..bf3ead2 100644 (file)
@@ -353,6 +353,20 @@ public:
    */
   bool IsBackgroundEnabled() const;
 
+  /**
+   * @brief Sets whether the text has a markup-processor or not.
+   *
+   * @param[in] enabled true if the text has a markup-processor.
+   */
+  void SetMarkupProcessorEnabled(bool enabled);
+
+  /**
+   * @brief Returns whether the text has a markup-processor or not.
+   *
+   * @return whether the text has a markup-processor or not.
+   */
+  bool IsMarkupProcessorEnabled() const;
+
 protected:
   /**
    * @brief A reference counted object may only be deleted by calling Unreference().
@@ -407,6 +421,7 @@ public:
   bool mUnderlineEnabled : 1;  ///< Underline enabled flag
   bool mUnderlineColorSet : 1; ///< Has the underline color been explicitly set?
   bool mBackgroundEnabled : 1; ///< Background enabled flag
+  bool mMarkupProcessorEnabled : 1; ///< Markup-processor enabled flag
 };
 
 } // namespace Text
index 19e08b2..870ac09 100644 (file)
@@ -528,12 +528,12 @@ void TextVisual::UpdateRenderer()
         shadowEnabled = true;
       }
 
-      const bool underlineEnabled  = mController->GetTextModel()->IsUnderlineEnabled();
-      const bool outlineEnabled    = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
-      const bool backgroundEnabled = mController->GetTextModel()->IsBackgroundEnabled();
-      ;
+      const bool underlineEnabled       = mController->GetTextModel()->IsUnderlineEnabled();
+      const bool outlineEnabled         = (mController->GetTextModel()->GetOutlineWidth() > Math::MACHINE_EPSILON_1);
+      const bool backgroundEnabled      = mController->GetTextModel()->IsBackgroundEnabled();
+      const bool markupProcessorEnabled = mController->IsMarkupProcessorEnabled();
 
-      const bool styleEnabled = (shadowEnabled || underlineEnabled || outlineEnabled || backgroundEnabled);
+      const bool styleEnabled = (shadowEnabled || underlineEnabled || outlineEnabled || backgroundEnabled || markupProcessorEnabled);
 
       AddRenderer(control, relayoutSize, hasMultipleTextColors, containsColorGlyph, styleEnabled);