Fix text AutoScroll ellipsis issue
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / layouts / layout-engine.cpp
index 7610b67..39af47c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -28,7 +28,9 @@
 #include <dali-toolkit/internal/text/bidirectional-support.h>
 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
+#include <dali-toolkit/internal/text/layouts/layout-engine-helper-functions.h>
 #include <dali-toolkit/internal/text/layouts/layout-parameters.h>
+#include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
 
 namespace Dali
 {
@@ -36,12 +38,20 @@ namespace Toolkit
 {
 namespace Text
 {
-float GetLineHeight(const LineRun lineRun)
+float GetLineHeight(const LineRun lineRun, bool isLastLine)
 {
   // The line height is the addition of the line ascender, the line descender and the line spacing.
   // However, the line descender has a negative value, hence the subtraction.
-  return lineRun.ascender - lineRun.descender + lineRun.lineSpacing;
+  // In case this is the only/last line then line spacing should be ignored.
+  float lineHeight = lineRun.ascender - lineRun.descender;
+
+  if(!isLastLine || lineRun.lineSpacing > 0)
+  {
+    lineHeight += lineRun.lineSpacing;
+  }
+  return lineHeight;
 }
+
 namespace Layout
 {
 namespace
@@ -50,12 +60,13 @@ namespace
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_LAYOUT");
 #endif
 
-const float              MAX_FLOAT      = std::numeric_limits<float>::max();
-const CharacterDirection LTR            = false;
-const CharacterDirection RTL            = !LTR;
-const float              LINE_SPACING   = 0.f;
-const float              MIN_LINE_SIZE  = 0.f;
-const Character          HYPHEN_UNICODE = 0x002D;
+const float              MAX_FLOAT          = std::numeric_limits<float>::max();
+const CharacterDirection LTR                = false;
+const CharacterDirection RTL                = !LTR;
+const float              LINE_SPACING       = 0.f;
+const float              MIN_LINE_SIZE      = 0.f;
+const Character          HYPHEN_UNICODE     = 0x002D;
+const float              RELATIVE_LINE_SIZE = 1.f;
 
 inline bool isEmptyLineAtLast(const Vector<LineRun>& lines, const Vector<LineRun>::Iterator& line)
 {
@@ -86,7 +97,8 @@ struct LineLayout
     glyphIndexInSecondHalfLine{0u},
     characterIndexInSecondHalfLine{0u},
     numberOfGlyphsInSecondHalfLine{0u},
-    numberOfCharactersInSecondHalfLine{0u}
+    numberOfCharactersInSecondHalfLine{0u},
+    relativeLineSize{1.0f}
 
   {
   }
@@ -109,6 +121,7 @@ struct LineLayout
     characterIndexInSecondHalfLine     = 0u;
     numberOfGlyphsInSecondHalfLine     = 0u;
     numberOfCharactersInSecondHalfLine = 0u;
+    relativeLineSize                   = 1.0f;
   }
 
   GlyphIndex         glyphIndex;                ///< Index of the first glyph to be laid-out.
@@ -129,6 +142,8 @@ struct LineLayout
   CharacterIndex characterIndexInSecondHalfLine;     ///< Index of the first character to be laid-out for the second half of line.
   Length         numberOfGlyphsInSecondHalfLine;     ///< The number of glyph which fit in one line for the second half of line.
   Length         numberOfCharactersInSecondHalfLine; ///< The number of characters which fit in one line for the second half of line.
+
+  float relativeLineSize; ///< The relative line size to be applied for this line.
 };
 
 struct LayoutBidiParameters
@@ -153,8 +168,53 @@ struct Engine::Impl
   : mLayout{Layout::Engine::SINGLE_LINE_BOX},
     mCursorWidth{0.f},
     mDefaultLineSpacing{LINE_SPACING},
-    mDefaultLineSize{MIN_LINE_SIZE}
+    mDefaultLineSize{MIN_LINE_SIZE},
+    mRelativeLineSize{RELATIVE_LINE_SIZE}
+  {
+  }
+
+  /**
+   * @brief get the line spacing.
+   *
+   * @param[in] textSize The text size.
+   * @param[in] relativeLineSize The relative line size to be applied.
+   * @return the line spacing value.
+   */
+  float GetLineSpacing(float textSize, float relativeLineSize)
   {
+    float lineSpacing;
+    float relTextSize;
+
+    // Sets the line size
+    lineSpacing = mDefaultLineSize - textSize;
+    lineSpacing = lineSpacing < 0.f ? 0.f : lineSpacing;
+
+    // Add the line spacing
+    lineSpacing += mDefaultLineSpacing;
+
+    //subtract line spcaing if relativeLineSize < 1 & larger than min height
+    relTextSize = textSize * relativeLineSize;
+    if(relTextSize > mDefaultLineSize)
+    {
+      if(relativeLineSize < 1)
+      {
+        //subtract the difference (always will be positive)
+        lineSpacing -= (textSize - relTextSize);
+      }
+      else
+      {
+        //reverse the addition in the top.
+        if(mDefaultLineSize > textSize)
+        {
+          lineSpacing -= mDefaultLineSize - textSize;
+        }
+
+        //add difference instead
+        lineSpacing += relTextSize - textSize;
+      }
+    }
+
+    return lineSpacing;
   }
 
   /**
@@ -185,12 +245,7 @@ struct Engine::Impl
     // Sets the minimum descender.
     lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
 
-    // Sets the line size
-    lineLayout.lineSpacing = mDefaultLineSize - (lineLayout.ascender + -lineLayout.descender);
-    lineLayout.lineSpacing = lineLayout.lineSpacing < 0.f ? 0.f : lineLayout.lineSpacing;
-
-    // Add the line spacing
-    lineLayout.lineSpacing += mDefaultLineSpacing;
+    lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender, lineLayout.relativeLineSize);
   }
 
   /**
@@ -250,10 +305,16 @@ struct Engine::Impl
 
     const float      outlineWidth                = static_cast<float>(parameters.textModel->GetOutlineWidth());
     const GlyphIndex lastGlyphOfParagraphPlusOne = parameters.startGlyphIndex + parameters.numberOfGlyphs;
+    const float      modelCharacterSpacing       = parameters.textModel->mVisualModel->GetCharacterSpacing();
+
+    // Get the character-spacing runs.
+    const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
 
     CharacterIndex characterLogicalIndex = 0u;
     CharacterIndex characterVisualIndex  = 0u;
 
+    float calculatedAdvance = 0.f;
+
     // If there are characters in the second half of Line then the first visual index mapped from visualToLogicalMapSecondHalf
     // Otherwise maps the first visual index from visualToLogicalMap.
     // This is to initialize the first visual index.
@@ -278,7 +339,9 @@ struct Engine::Impl
         {
           const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
 
-          whiteSpaceLengthEndOfLine += glyphInfo.advance;
+          const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+          calculatedAdvance            = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
+          whiteSpaceLengthEndOfLine += calculatedAdvance;
 
           ++characterLogicalIndex;
           characterVisualIndex = bidirectionalLineInfo.characterRunForSecondHalfLine.characterIndex + *(bidirectionalLineInfo.visualToLogicalMapSecondHalf + characterLogicalIndex);
@@ -299,7 +362,9 @@ struct Engine::Impl
         {
           const GlyphInfo& glyphInfo = *(glyphsBuffer + *(charactersToGlyphsBuffer + characterVisualIndex));
 
-          whiteSpaceLengthEndOfLine += glyphInfo.advance;
+          const float characterSpacing = GetGlyphCharacterSpacing(characterVisualIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+          calculatedAdvance            = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, glyphInfo.advance);
+          whiteSpaceLengthEndOfLine += calculatedAdvance;
 
           ++characterLogicalIndex;
           characterVisualIndex = bidirectionalLineInfo.characterRun.characterIndex + *(bidirectionalLineInfo.visualToLogicalMap + characterLogicalIndex);
@@ -316,11 +381,14 @@ struct Engine::Impl
                                                                   charactersPerGlyphBuffer);
 
     GlyphMetrics glyphMetrics;
+    const float  characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+    calculatedAdvance             = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
     GetGlyphsMetrics(glyphIndex,
                      numberOfGLyphsInGroup,
                      glyphMetrics,
                      glyphsBuffer,
-                     mMetrics);
+                     mMetrics,
+                     calculatedAdvance);
 
     float penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
 
@@ -345,11 +413,14 @@ struct Engine::Impl
         characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
 
         GlyphMetrics glyphMetrics;
+        const float  characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+        calculatedAdvance             = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
         GetGlyphsMetrics(glyphIndex,
                          numberOfGLyphsInGroup,
                          glyphMetrics,
                          glyphsBuffer,
-                         mMetrics);
+                         mMetrics,
+                         calculatedAdvance);
 
         if(isWhiteSpace)
         {
@@ -405,11 +476,14 @@ struct Engine::Impl
       characterLogicalIndex += *(charactersPerGlyphBuffer + glyphIndex + numberOfGLyphsInGroup - 1u);
 
       GlyphMetrics glyphMetrics;
+      const float  characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+      calculatedAdvance             = GetCalculatedAdvance(*(textBuffer + characterVisualIndex), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
       GetGlyphsMetrics(glyphIndex,
                        numberOfGLyphsInGroup,
                        glyphMetrics,
                        glyphsBuffer,
-                       mMetrics);
+                       mMetrics,
+                       calculatedAdvance);
 
       if(isWhiteSpace)
       {
@@ -639,13 +713,24 @@ struct Engine::Impl
     float widthFirstHalf = ((ellipsisPosition != DevelText::EllipsisPosition::MIDDLE) ? targetWidth : targetWidth - std::floor(targetWidth / 2));
 
     bool isSecondHalf = false;
+    // Character Spacing
+    const float             modelCharacterSpacing     = parameters.textModel->mVisualModel->GetCharacterSpacing();
+    float                   calculatedAdvance         = 0.f;
+    Vector<CharacterIndex>& glyphToCharacterMap       = parameters.textModel->mVisualModel->mGlyphsToCharacters;
+    const CharacterIndex*   glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
+
+    // Get the character-spacing runs.
+    const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = parameters.textModel->mVisualModel->GetCharacterSpacingGlyphRuns();
 
     GlyphMetrics glyphMetrics;
+    const float  characterSpacing = GetGlyphCharacterSpacing(lineLayout.glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+    calculatedAdvance             = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + lineLayout.glyphIndex))), characterSpacing, (*(glyphsBuffer + lineLayout.glyphIndex)).advance);
     GetGlyphsMetrics(lineLayout.glyphIndex,
                      numberOfGLyphsInGroup,
                      glyphMetrics,
                      glyphsBuffer,
-                     mMetrics);
+                     mMetrics,
+                     calculatedAdvance);
 
     // Set the direction of the first character of the line.
     lineLayout.characterIndex = *(glyphsToCharactersBuffer + lineLayout.glyphIndex);
@@ -660,6 +745,8 @@ struct Engine::Impl
     // It needs to add as well space for the cursor if the text is in edit mode and extra space in case the text is outlined.
     tmpLineLayout.penX = -glyphMetrics.xBearing + mCursorWidth + outlineWidth;
 
+    tmpLineLayout.relativeLineSize = lineLayout.relativeLineSize;
+
     // Calculate the line height if there is no characters.
     FontId lastFontId = glyphMetrics.fontId;
     UpdateLineHeight(glyphMetrics, tmpLineLayout);
@@ -680,11 +767,14 @@ struct Engine::Impl
                                                                     charactersPerGlyphBuffer);
 
       GlyphMetrics glyphMetrics;
+      const float  characterSpacing = GetGlyphCharacterSpacing(glyphIndex, characterSpacingGlyphRuns, modelCharacterSpacing);
+      calculatedAdvance             = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndex))), characterSpacing, (*(glyphsBuffer + glyphIndex)).advance);
       GetGlyphsMetrics(glyphIndex,
                        numberOfGLyphsInGroup,
                        glyphMetrics,
                        glyphsBuffer,
-                       mMetrics);
+                       mMetrics,
+                       calculatedAdvance);
 
       const bool isLastGlyph = glyphIndex + numberOfGLyphsInGroup == totalNumberOfGlyphs;
 
@@ -739,7 +829,8 @@ struct Engine::Impl
       if(isWhiteSpace)
       {
         // Add the length to the length of white spaces at the end of the line.
-        tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance; // The advance is used as the width is always zero for the white spaces.
+        tmpLineLayout.whiteSpaceLengthEndOfLine += glyphMetrics.advance;
+        // The advance is used as the width is always zero for the white spaces.
       }
       else
       {
@@ -777,11 +868,14 @@ struct Engine::Impl
         while(tmpLineLayout.length + tmpLineLayout.whiteSpaceLengthEndOfLine > targetWidth && glyphIndexToRemove < glyphIndex)
         {
           GlyphMetrics glyphMetrics;
+          const float  characterSpacing = GetGlyphCharacterSpacing(glyphIndexToRemove, characterSpacingGlyphRuns, modelCharacterSpacing);
+          calculatedAdvance             = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + glyphIndexToRemove))), characterSpacing, (*(glyphsBuffer + glyphIndexToRemove)).advance);
           GetGlyphsMetrics(glyphIndexToRemove,
                            numberOfGLyphsInGroup,
                            glyphMetrics,
                            glyphsBuffer,
-                           mMetrics);
+                           mMetrics,
+                           calculatedAdvance);
 
           const Length numberOfGLyphsInGroup = GetNumberOfGlyphsOfGroup(glyphIndexToRemove,
                                                                         lastGlyphOfParagraphPlusOne,
@@ -1008,16 +1102,14 @@ struct Engine::Impl
     const GlyphInfo& glyph = *(glyphsBuffer + startIndexForGlyph);
     float            penX  = -glyph.xBearing + mCursorWidth + outlineWidth; //
 
-    for(GlyphIndex i = 0u; i < numberOfGlyphs; ++i)
-    {
-      const GlyphInfo& glyph    = *(glyphsBuffer + startIndexForGlyph + i);
-      Vector2&         position = *(glyphPositionsBuffer + startIndexForGlyphPositions + i);
-
-      position.x = penX + glyph.xBearing;
-      position.y = -glyph.yBearing;
-
-      penX += (glyph.advance + interGlyphExtraAdvance);
-    }
+    CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
+                               layoutParameters.textModel->mLogicalModel,
+                               interGlyphExtraAdvance,
+                               numberOfGlyphs,
+                               startIndexForGlyph, // startIndexForGlyph is the index of the first glyph in the line
+                               startIndexForGlyphPositions,
+                               glyphPositionsBuffer,
+                               penX);
 
     if(layout.isSplitToTwoHalves)
     {
@@ -1025,16 +1117,14 @@ struct Engine::Impl
       const Length     numberOfGlyphsInSecondHalfLine         = layout.numberOfGlyphsInSecondHalfLine;
       const GlyphIndex startIndexForGlyphPositionsnSecondHalf = layout.glyphIndexInSecondHalfLine - layoutParameters.startGlyphIndex;
 
-      for(GlyphIndex i = 0u; i < numberOfGlyphsInSecondHalfLine; ++i)
-      {
-        const GlyphInfo& glyph    = *(glyphsBuffer + startIndexForGlyphInSecondHalf + i);
-        Vector2&         position = *(glyphPositionsBuffer + startIndexForGlyphPositionsnSecondHalf + i);
-
-        position.x = penX + glyph.xBearing;
-        position.y = -glyph.yBearing;
-
-        penX += (glyph.advance + interGlyphExtraAdvance);
-      }
+      CalculateGlyphPositionsLTR(layoutParameters.textModel->mVisualModel,
+                                 layoutParameters.textModel->mLogicalModel,
+                                 interGlyphExtraAdvance,
+                                 numberOfGlyphsInSecondHalfLine,
+                                 startIndexForGlyphInSecondHalf, // startIndexForGlyph is the index of the first glyph in the line
+                                 startIndexForGlyphPositionsnSecondHalf,
+                                 glyphPositionsBuffer,
+                                 penX);
     }
   }
 
@@ -1043,11 +1133,9 @@ struct Engine::Impl
                          LayoutBidiParameters& layoutBidiParameters,
                          const LineLayout&     layout)
   {
-    const Character* const          textBuffer               = layoutParameters.textModel->mLogicalModel->mText.Begin();
     const BidirectionalLineInfoRun& bidiLine                 = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo[layoutBidiParameters.bidiLineIndex];
     const GlyphInfo* const          glyphsBuffer             = layoutParameters.textModel->mVisualModel->mGlyphs.Begin();
     const GlyphIndex* const         charactersToGlyphsBuffer = layoutParameters.textModel->mVisualModel->mCharactersToGlyph.Begin();
-    const Length* const             glyphsPerCharacterBuffer = layoutParameters.textModel->mVisualModel->mGlyphsPerCharacter.Begin();
 
     CharacterIndex characterLogicalIndex = 0u;
     CharacterIndex characterVisualIndex  = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
@@ -1057,20 +1145,14 @@ struct Engine::Impl
 
     if(layout.isSplitToTwoHalves)
     {
-      while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
-      {
-        const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
-        const GlyphInfo& glyph      = *(glyphsBuffer + glyphIndex);
-
-        Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
-        position.x        = penX;
-        position.y        = -glyph.yBearing;
-
-        penX += glyph.advance;
-
-        ++characterLogicalIndex;
-        characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
-      }
+      CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
+                                 layoutParameters.textModel->mLogicalModel,
+                                 layoutBidiParameters.bidiLineIndex,
+                                 layoutParameters.startGlyphIndex,
+                                 glyphPositionsBuffer,
+                                 characterVisualIndex,
+                                 characterLogicalIndex,
+                                 penX);
     }
 
     if(characterLogicalIndex == bidiLine.characterRunForSecondHalfLine.numberOfCharacters)
@@ -1079,20 +1161,14 @@ struct Engine::Impl
       characterLogicalIndex = 0u;
       characterVisualIndex  = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
 
-      while(TextAbstraction::IsWhiteSpace(*(textBuffer + characterVisualIndex)))
-      {
-        const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
-        const GlyphInfo& glyph      = *(glyphsBuffer + glyphIndex);
-
-        Vector2& position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
-        position.x        = penX;
-        position.y        = -glyph.yBearing;
-
-        penX += glyph.advance;
-
-        ++characterLogicalIndex;
-        characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
-      }
+      CalculateGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
+                                 layoutParameters.textModel->mLogicalModel,
+                                 layoutBidiParameters.bidiLineIndex,
+                                 layoutParameters.startGlyphIndex,
+                                 glyphPositionsBuffer,
+                                 characterVisualIndex,
+                                 characterLogicalIndex,
+                                 penX);
     }
 
     const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex);
@@ -1103,59 +1179,28 @@ struct Engine::Impl
     // Traverses the characters of the right to left paragraph.
     if(layout.isSplitToTwoHalves && !extendedToSecondHalf)
     {
-      for(; characterLogicalIndex < bidiLine.characterRunForSecondHalfLine.numberOfCharacters;
-          ++characterLogicalIndex)
-      {
-        // Convert the character in the logical order into the character in the visual order.
-        const CharacterIndex characterVisualIndex = bidiLine.characterRunForSecondHalfLine.characterIndex + *(bidiLine.visualToLogicalMapSecondHalf + characterLogicalIndex);
-
-        // Get the number of glyphs of the character.
-        const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
-
-        for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
-        {
-          // Convert the character in the visual order into the glyph in the visual order.
-          const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
-
-          DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
-
-          const GlyphInfo& glyph    = *(glyphsBuffer + glyphIndex);
-          Vector2&         position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
-
-          position.x = penX + glyph.xBearing;
-          position.y = -glyph.yBearing;
-
-          penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
-        }
-      }
+      TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
+                                              layoutParameters.textModel->mLogicalModel->mText.Begin(),
+                                              layoutParameters.startGlyphIndex,
+                                              layoutParameters.interGlyphExtraAdvance,
+                                              bidiLine.characterRunForSecondHalfLine,
+                                              bidiLine.visualToLogicalMapSecondHalf,
+                                              glyphPositionsBuffer,
+                                              characterLogicalIndex,
+                                              penX);
     }
 
     characterLogicalIndex = extendedToSecondHalf ? characterLogicalIndex : 0u;
-    for(; characterLogicalIndex < bidiLine.characterRun.numberOfCharacters;
-        ++characterLogicalIndex)
-    {
-      // Convert the character in the logical order into the character in the visual order.
-      const CharacterIndex characterVisualIndex = bidiLine.characterRun.characterIndex + *(bidiLine.visualToLogicalMap + characterLogicalIndex);
-
-      // Get the number of glyphs of the character.
-      const Length numberOfGlyphs = *(glyphsPerCharacterBuffer + characterVisualIndex);
-
-      for(GlyphIndex index = 0u; index < numberOfGlyphs; ++index)
-      {
-        // Convert the character in the visual order into the glyph in the visual order.
-        const GlyphIndex glyphIndex = *(charactersToGlyphsBuffer + characterVisualIndex) + index;
-
-        DALI_ASSERT_DEBUG(glyphIndex < layoutParameters.textModel->mVisualModel->mGlyphs.Count());
 
-        const GlyphInfo& glyph    = *(glyphsBuffer + glyphIndex);
-        Vector2&         position = *(glyphPositionsBuffer + glyphIndex - layoutParameters.startGlyphIndex);
-
-        position.x = penX + glyph.xBearing;
-        position.y = -glyph.yBearing;
-
-        penX += (glyph.advance + layoutParameters.interGlyphExtraAdvance);
-      }
-    }
+    TraversesCharactersForGlyphPositionsRTL(layoutParameters.textModel->mVisualModel,
+                                            layoutParameters.textModel->mLogicalModel->mText.Begin(),
+                                            layoutParameters.startGlyphIndex,
+                                            layoutParameters.interGlyphExtraAdvance,
+                                            bidiLine.characterRun,
+                                            bidiLine.visualToLogicalMap,
+                                            glyphPositionsBuffer,
+                                            characterLogicalIndex,
+                                            penX);
   }
 
   /**
@@ -1215,21 +1260,30 @@ struct Engine::Impl
                     Length&                           numberOfLines,
                     float                             penY,
                     bool&                             isAutoScrollEnabled,
+                    bool                              isAutoScrollMaxTextureExceeded,
                     DevelText::EllipsisPosition::Type ellipsisPosition,
                     bool                              enforceEllipsisInSingleLine)
   {
-    const bool ellipsis    = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? (penY - layout.descender > layoutParameters.boundingBox.height) : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
+    const bool ellipsis    = enforceEllipsisInSingleLine || (isAutoScrollEnabled ? isAutoScrollMaxTextureExceeded : ((penY - layout.descender > layoutParameters.boundingBox.height) || ((mLayout == SINGLE_LINE_BOX) && (layout.length > layoutParameters.boundingBox.width))));
     const bool isMultiline = !enforceEllipsisInSingleLine && (mLayout == MULTI_LINE_BOX);
     if(ellipsis && (ellipsisPosition == DevelText::EllipsisPosition::END || !isMultiline))
     {
-      isAutoScrollEnabled = false;
-      // Do not layout more lines if ellipsis is enabled.
+      if(penY - layout.descender > layoutParameters.boundingBox.height)
+      {
+        // Even if auto scroll is enabled and text is bigger than max texture size,
+        // if the the height is small, auto scroll should not work.
+        isAutoScrollEnabled = false;
+      }
 
+      // Do not layout more lines if ellipsis is enabled.
       // The last line needs to be completely filled with characters.
       // Part of a word may be used.
 
       LineRun*   lineRun = nullptr;
       LineLayout ellipsisLayout;
+
+      ellipsisLayout.relativeLineSize = layout.relativeLineSize;
+
       if(0u != numberOfLines)
       {
         // Get the last line and layout it again with the 'completelyFill' flag to true.
@@ -1281,7 +1335,16 @@ struct Engine::Impl
       layoutSize.width = layoutParameters.boundingBox.width;
       if(layoutSize.height < Math::MACHINE_EPSILON_1000)
       {
-        layoutSize.height += GetLineHeight(*lineRun);
+        layoutSize.height += GetLineHeight(*lineRun, true);
+      }
+      else
+      {
+        //when we apply ellipsis, the last line should not take negative linespacing into account for layoutSize.height calculation
+        //usually we don't includ it in normal cases using GetLineHeight()
+        if(lineRun->lineSpacing < 0)
+        {
+          layoutSize.height -= lineRun->lineSpacing;
+        }
       }
 
       const Vector<BidirectionalLineInfoRun>& bidirectionalLinesInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
@@ -1377,10 +1440,7 @@ struct Engine::Impl
     lineRun.direction = layout.direction;
     lineRun.ellipsis  = false;
 
-    lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
-    lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
-
-    lineRun.lineSpacing += mDefaultLineSpacing;
+    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
 
     // Update the actual size.
     if(lineRun.width > layoutSize.width)
@@ -1388,7 +1448,7 @@ struct Engine::Impl
       layoutSize.width = lineRun.width;
     }
 
-    layoutSize.height += GetLineHeight(lineRun);
+    layoutSize.height += GetLineHeight(lineRun, isLastLine);
   }
 
   /**
@@ -1434,12 +1494,13 @@ struct Engine::Impl
     lineRun.direction                       = LTR;
     lineRun.ellipsis                        = false;
 
-    lineRun.lineSpacing = mDefaultLineSize - (lineRun.ascender + -lineRun.descender);
-    lineRun.lineSpacing = lineRun.lineSpacing < 0.f ? 0.f : lineRun.lineSpacing;
+    BoundedParagraphRun currentParagraphRun;
+    LineLayout          tempLineLayout;
+    (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(&currentParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
 
-    lineRun.lineSpacing += mDefaultLineSpacing;
+    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
 
-    layoutSize.height += GetLineHeight(lineRun);
+    layoutSize.height += GetLineHeight(lineRun, true);
   }
 
   /**
@@ -1456,14 +1517,15 @@ struct Engine::Impl
         it != endIt;
         ++it)
     {
-      const LineRun& line = *it;
+      const LineRun& line       = *it;
+      bool           isLastLine = (it + 1 == endIt);
 
       if(line.width > layoutSize.width)
       {
         layoutSize.width = line.width;
       }
 
-      layoutSize.height += GetLineHeight(line);
+      layoutSize.height += GetLineHeight(line, isLastLine);
     }
   }
 
@@ -1496,10 +1558,56 @@ struct Engine::Impl
     }
   }
 
+  /**
+   * @brief Sets the relative line size for the LineLayout
+   *
+   * @param[in] currentParagraphRun Contains the bounded paragraph for this line layout.
+   * @param[in,out] lineLayout The line layout to be updated.
+   */
+  void SetRelativeLineSize(BoundedParagraphRun* currentParagraphRun, LineLayout& lineLayout)
+  {
+    lineLayout.relativeLineSize = mRelativeLineSize;
+
+    if(currentParagraphRun != nullptr && currentParagraphRun->relativeLineSizeDefined)
+    {
+      lineLayout.relativeLineSize = currentParagraphRun->relativeLineSize;
+    }
+  }
+
+  /**
+   * @brief Get the bounded paragraph for the characterIndex if exists.
+   *
+   * @param[in] boundedParagraphRuns The bounded paragraph list to search in.
+   * @param[in] characterIndex The character index to get bounded paragraph for.
+   * @param[out] currentParagraphRun Contains the bounded paragraph if found for the characterIndex.
+   *
+   * @return returns true if a bounded paragraph was found.
+   */
+  bool GetBoundedParagraph(const Vector<BoundedParagraphRun> boundedParagraphRuns, CharacterIndex characterIndex, BoundedParagraphRun& currentParagraphRun)
+  {
+    for(Vector<BoundedParagraphRun>::Iterator it    = boundedParagraphRuns.Begin(),
+                                              endIt = boundedParagraphRuns.End();
+        it != endIt;
+        ++it)
+    {
+      BoundedParagraphRun& tempParagraphRun = *it;
+
+      if(characterIndex >= tempParagraphRun.characterRun.characterIndex &&
+         characterIndex < (tempParagraphRun.characterRun.characterIndex + tempParagraphRun.characterRun.numberOfCharacters))
+      {
+        currentParagraphRun = tempParagraphRun;
+        return true;
+      }
+    }
+
+    return false;
+  }
+
   bool LayoutText(Parameters&                       layoutParameters,
                   Size&                             layoutSize,
                   bool                              elideTextEnabled,
                   bool&                             isAutoScrollEnabled,
+                  bool                              isAutoScrollMaxTextureExceeded,
                   DevelText::EllipsisPosition::Type ellipsisPosition)
   {
     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->LayoutText\n");
@@ -1514,7 +1622,8 @@ struct Engine::Impl
     layoutParameters.textModel->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
     layoutParameters.textModel->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
 
-    Vector<LineRun>& lines = layoutParameters.textModel->mVisualModel->mLines;
+    Vector<LineRun>&                   lines                = layoutParameters.textModel->mVisualModel->mLines;
+    const Vector<BoundedParagraphRun>& boundedParagraphRuns = layoutParameters.textModel->GetBoundedParagraphRuns();
 
     if(0u == layoutParameters.numberOfGlyphs)
     {
@@ -1574,7 +1683,7 @@ struct Engine::Impl
     // Retrieve BiDi info.
     const bool hasBidiParagraphs = !layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo.Empty();
 
-    const CharacterIndex* const                  glyphsToCharactersBuffer    = hasBidiParagraphs ? layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin() : nullptr;
+    const CharacterIndex* const                  glyphsToCharactersBuffer    = layoutParameters.textModel->mVisualModel->mGlyphsToCharacters.Begin();
     const Vector<BidirectionalParagraphInfoRun>& bidirectionalParagraphsInfo = layoutParameters.textModel->mLogicalModel->mBidirectionalParagraphInfo;
     const Vector<BidirectionalLineInfoRun>&      bidirectionalLinesInfo      = layoutParameters.textModel->mLogicalModel->mBidirectionalLineInfo;
 
@@ -1678,6 +1787,10 @@ struct Engine::Impl
       LineLayout layout;
       layout.direction  = layoutBidiParameters.paragraphDirection;
       layout.glyphIndex = index;
+
+      BoundedParagraphRun currentParagraphRun;
+      (GetBoundedParagraph(boundedParagraphRuns, *(glyphsToCharactersBuffer + index), currentParagraphRun) ? SetRelativeLineSize(&currentParagraphRun, layout) : SetRelativeLineSize(nullptr, layout));
+
       GetLineLayoutForBox(layoutParameters,
                           layoutBidiParameters,
                           layout,
@@ -1692,6 +1805,14 @@ struct Engine::Impl
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "  number of characters %d\n", layout.numberOfCharacters);
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "                length %f\n", layout.length);
 
+      CharacterIndex lastCharacterInParagraph = currentParagraphRun.characterRun.characterIndex + currentParagraphRun.characterRun.numberOfCharacters - 1;
+
+      //check if this is the last line in paragraph, if false we should use the default relative line size (the one set using the property)
+      if(lastCharacterInParagraph >= layout.characterIndex && lastCharacterInParagraph < layout.characterIndex + layout.numberOfCharacters)
+      {
+        layout.relativeLineSize = mRelativeLineSize;
+      }
+
       if(0u == layout.numberOfGlyphs + layout.numberOfGlyphsInSecondHalfLine)
       {
         // The width is too small and no characters are laid-out.
@@ -1726,6 +1847,7 @@ struct Engine::Impl
                                 numberOfLines,
                                 penY,
                                 isAutoScrollEnabled,
+                                isAutoScrollMaxTextureExceeded,
                                 ellipsisPosition,
                                 false);
       }
@@ -1744,6 +1866,7 @@ struct Engine::Impl
                                   numberOfLines,
                                   penY,
                                   isAutoScrollEnabled,
+                                  isAutoScrollMaxTextureExceeded,
                                   ellipsisPosition,
                                   true);
         }
@@ -1835,14 +1958,7 @@ struct Engine::Impl
         }
 
         // Updates the vertical pen's position.
-        penY += -layout.descender + layout.lineSpacing + mDefaultLineSpacing;
-        // If there is a defaultLineSize, updates the pen's position.
-        if(mDefaultLineSize > 0.f)
-        {
-          float lineSpacing = mDefaultLineSize - (layout.ascender + -layout.descender);
-          lineSpacing       = lineSpacing < 0.f ? 0.f : lineSpacing;
-          penY += lineSpacing;
-        }
+        penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
 
         // Increase the glyph index.
         index = nextIndex;
@@ -2101,6 +2217,7 @@ struct Engine::Impl
   float mDefaultLineSize;
 
   IntrusivePtr<Metrics> mMetrics;
+  float                 mRelativeLineSize;
 };
 
 Engine::Engine()
@@ -2144,12 +2261,14 @@ bool Engine::LayoutText(Parameters&                       layoutParameters,
                         Size&                             layoutSize,
                         bool                              elideTextEnabled,
                         bool&                             isAutoScrollEnabled,
+                        bool                              isAutoScrollMaxTextureExceeded,
                         DevelText::EllipsisPosition::Type ellipsisPosition)
 {
   return mImpl->LayoutText(layoutParameters,
                            layoutSize,
                            elideTextEnabled,
                            isAutoScrollEnabled,
+                           isAutoScrollMaxTextureExceeded,
                            ellipsisPosition);
 }
 
@@ -2192,6 +2311,16 @@ float Engine::GetDefaultLineSize() const
   return mImpl->mDefaultLineSize;
 }
 
+void Engine::SetRelativeLineSize(float relativeLineSize)
+{
+  mImpl->mRelativeLineSize = relativeLineSize;
+}
+
+float Engine::GetRelativeLineSize() const
+{
+  return mImpl->mRelativeLineSize;
+}
+
 } // namespace Layout
 
 } // namespace Text