Support paragraph attribute: relative line height 13/272113/4
authorabdullah <abdullahhasan10@gmail.com>
Wed, 9 Mar 2022 10:27:50 +0000 (13:27 +0300)
committerAbdulleh Ghujeh <abdullahhasan10@gmail.com>
Mon, 28 Mar 2022 12:51:19 +0000 (15:51 +0300)
<p rel-line-height=2.0>Hello</p>World

Change-Id: I3824dd0d39021222a94e3122c4d88d94da5bc0b2

automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
dali-toolkit/internal/text/bounded-paragraph-run.h
dali-toolkit/internal/text/layouts/layout-engine.cpp
dali-toolkit/internal/text/markup-processor-paragraph.cpp
dali-toolkit/internal/text/markup-processor-paragraph.h

index 9d9567b..fe83ae8 100644 (file)
@@ -5765,6 +5765,55 @@ int UtcDaliToolkitTextEditorUnderlineTypesGeneration3(void)
   END_TEST;
 }
 
+int UtcDaliToolkitTextEditorMarkupRelativeLineHeight(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextEditorMarkupRelativeLineHeight");
+
+  TextEditor editor = TextEditor::New();
+  editor.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  editor.SetProperty(TextEditor::Property::POINT_SIZE, 10);
+  editor.SetProperty(TextEditor::Property::TEXT, "line 1\nline 2\nline 3\nline 4\nline 5");
+  editor.SetProperty(DevelTextEditor::Property::RELATIVE_LINE_SIZE, 1.0f);
+  editor.SetProperty(DevelTextEditor::Property::ELLIPSIS, false);
+  editor.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+
+  TextEditor editorSingleLineParagraph = TextEditor::New();
+  editorSingleLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  editorSingleLineParagraph.SetProperty(TextEditor::Property::POINT_SIZE, 10);
+  editorSingleLineParagraph.SetProperty(TextEditor::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line 2</p>line 3<p rel-line-height=3>line 4</p>line 5");
+  editorSingleLineParagraph.SetProperty(DevelTextEditor::Property::RELATIVE_LINE_SIZE, 1.0f);
+  editorSingleLineParagraph.SetProperty(DevelTextEditor::Property::ELLIPSIS, false);
+  editorSingleLineParagraph.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+
+  TextEditor editorMultiLineParagraph = TextEditor::New();
+  editorMultiLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  editorMultiLineParagraph.SetProperty(TextEditor::Property::POINT_SIZE, 10);
+  editorMultiLineParagraph.SetProperty(TextEditor::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line\n2</p>line 3<p rel-line-height=3>line\n4</p>line 5");
+  editorMultiLineParagraph.SetProperty(DevelTextEditor::Property::RELATIVE_LINE_SIZE, 1.0f);
+  editorMultiLineParagraph.SetProperty(DevelTextEditor::Property::ELLIPSIS, false);
+  editorMultiLineParagraph.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+
+  application.GetScene().Add(editor);
+  application.GetScene().Add(editorSingleLineParagraph);
+  application.GetScene().Add(editorMultiLineParagraph);
+  application.SendNotification();
+  application.Render();
+
+  Vector3 naturalSize               = editor.GetNaturalSize();
+  Vector3 relativeSingleNaturalSize = editorSingleLineParagraph.GetNaturalSize();
+  Vector3 relativeMultiNaturalSize  = editorMultiLineParagraph.GetNaturalSize();
+
+  float lineSize = naturalSize.y / 5.0f; //total size/number of lines
+
+  //no effect of relative line size for paragraph with single line
+  DALI_TEST_EQUALS(naturalSize.y, relativeSingleNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(lineSize*8.5f, relativeMultiNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliToolkitTextEditorRelativeLineHeight(void)
 {
   ToolkitTestApplication application;
index bbd3d77..30aaade 100644 (file)
@@ -2632,6 +2632,58 @@ int UtcDaliToolkitTextLabelStrikethroughGeneration(void)
   END_TEST;
 }
 
+int UtcDaliToolkitTextLabelMarkupRelativeLineHeight(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextLabelMarkupRelativeLineHeight");
+
+  TextLabel label = TextLabel::New();
+  label.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  label.SetProperty(TextLabel::Property::POINT_SIZE, 10);
+  label.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  label.SetProperty(TextLabel::Property::TEXT, "line 1\nline 2\nline 3\nline 4\nline 5");
+  label.SetProperty(DevelTextLabel::Property::RELATIVE_LINE_SIZE, 1.0f);
+  label.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  label.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+
+  TextLabel labelSingleLineParagraph = TextLabel::New();
+  labelSingleLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::POINT_SIZE, 10);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line 2</p>line 3<p rel-line-height=3>line 4</p>line 5");
+  labelSingleLineParagraph.SetProperty(DevelTextLabel::Property::RELATIVE_LINE_SIZE, 1.0f);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  labelSingleLineParagraph.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+
+  TextLabel labelMultiLineParagraph = TextLabel::New();
+  labelMultiLineParagraph.SetProperty(Actor::Property::SIZE, Vector2(200.0f, 300.f));
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::POINT_SIZE, 10);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::TEXT, "<p>line 1</p><p rel-line-height=0.5>line\n2</p>line 3<p rel-line-height=3>line\n4</p>line 5");
+  labelMultiLineParagraph.SetProperty(DevelTextLabel::Property::RELATIVE_LINE_SIZE, 1.0f);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  labelMultiLineParagraph.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+
+  application.GetScene().Add(label);
+  application.GetScene().Add(labelSingleLineParagraph);
+  application.GetScene().Add(labelMultiLineParagraph);
+  application.SendNotification();
+  application.Render();
+
+  Vector3 naturalSize               = label.GetNaturalSize();
+  Vector3 relativeSingleNaturalSize = labelSingleLineParagraph.GetNaturalSize();
+  Vector3 relativeMultiNaturalSize  = labelMultiLineParagraph.GetNaturalSize();
+
+  float lineSize = naturalSize.y / 5.0f; //total size/number of lines
+
+  //no effect of relative line size for paragraph with single line
+  DALI_TEST_EQUALS(naturalSize.y, relativeSingleNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(lineSize*8.5f, relativeMultiNaturalSize.y, Math::MACHINE_EPSILON_1000, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliToolkitTextLabelRelativeLineHeight(void)
 {
   ToolkitTestApplication application;
index ec15ff4..4b3150e 100644 (file)
@@ -48,13 +48,17 @@ struct BoundedParagraphRun
   BoundedParagraphRun()
   : characterRun{},
     horizontalAlignment(Text::HorizontalAlignment::BEGIN),
-    horizontalAlignmentDefined{false}
+    relativeLineSize(1),
+    horizontalAlignmentDefined{false},
+    relativeLineSizeDefined(false)
   {
   }
 
   CharacterRun                    characterRun;                   ///< The initial character index within the whole text and the number of characters of the run.
   Text::HorizontalAlignment::Type horizontalAlignment;            ///< The paragraph horizontal alignment. Values "BEGIN" "CENTER" "END".
+  float                           relativeLineSize;               ///< The relative line height to be used for this paragaraph.
   bool                            horizontalAlignmentDefined : 1; ///< Whether the horizontal alignment is defined.
+  bool                            relativeLineSizeDefined : 1;    ///< Whether the relative line height is defined for this paragraph.
 };
 
 } // namespace Text
index aff78c6..b701086 100644 (file)
@@ -96,7 +96,8 @@ struct LineLayout
     glyphIndexInSecondHalfLine{0u},
     characterIndexInSecondHalfLine{0u},
     numberOfGlyphsInSecondHalfLine{0u},
-    numberOfCharactersInSecondHalfLine{0u}
+    numberOfCharactersInSecondHalfLine{0u},
+    relativeLineSize{1.0f}
 
   {
   }
@@ -119,6 +120,7 @@ struct LineLayout
     characterIndexInSecondHalfLine     = 0u;
     numberOfGlyphsInSecondHalfLine     = 0u;
     numberOfCharactersInSecondHalfLine = 0u;
+    relativeLineSize                   = 1.0f;
   }
 
   GlyphIndex         glyphIndex;                ///< Index of the first glyph to be laid-out.
@@ -139,6 +141,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
@@ -172,9 +176,10 @@ struct Engine::Impl
    * @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 GetLineSpacing(float textSize, float relativeLineSize)
   {
     float lineSpacing;
     float relTextSize;
@@ -187,10 +192,10 @@ struct Engine::Impl
     lineSpacing += mDefaultLineSpacing;
 
     //subtract line spcaing if relativeLineSize < 1 & larger than min height
-    relTextSize = textSize * mRelativeLineSize;
+    relTextSize = textSize * relativeLineSize;
     if(relTextSize > mDefaultLineSize)
     {
-      if(mRelativeLineSize < 1)
+      if(relativeLineSize < 1)
       {
         //subtract the difference (always will be positive)
         lineSpacing -= (textSize - relTextSize);
@@ -239,7 +244,7 @@ struct Engine::Impl
     // Sets the minimum descender.
     lineLayout.descender = std::min(lineLayout.descender, fontMetrics.descender);
 
-    lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender);
+    lineLayout.lineSpacing = GetLineSpacing(lineLayout.ascender + -lineLayout.descender, lineLayout.relativeLineSize);
   }
 
   /**
@@ -727,6 +732,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);
@@ -1253,6 +1260,9 @@ struct Engine::Impl
 
       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.
@@ -1409,7 +1419,7 @@ struct Engine::Impl
     lineRun.direction = layout.direction;
     lineRun.ellipsis  = false;
 
-    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
+    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, layout.relativeLineSize);
 
     // Update the actual size.
     if(lineRun.width > layoutSize.width)
@@ -1463,7 +1473,11 @@ struct Engine::Impl
     lineRun.direction                       = LTR;
     lineRun.ellipsis                        = false;
 
-    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender);
+    BoundedParagraphRun currentParagraphRun;
+    LineLayout          tempLineLayout;
+    (GetBoundedParagraph(layoutParameters.textModel->GetBoundedParagraphRuns(), characterIndex, currentParagraphRun) ? SetRelativeLineSize(&currentParagraphRun, tempLineLayout) : SetRelativeLineSize(nullptr, tempLineLayout));
+
+    lineRun.lineSpacing = GetLineSpacing(lineRun.ascender + -lineRun.descender, tempLineLayout.relativeLineSize);
 
     layoutSize.height += GetLineHeight(lineRun, true);
   }
@@ -1523,6 +1537,51 @@ 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,
@@ -1541,7 +1600,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)
     {
@@ -1601,7 +1661,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;
 
@@ -1705,6 +1765,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,
@@ -1719,6 +1783,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.
@@ -1862,7 +1934,7 @@ struct Engine::Impl
         }
 
         // Updates the vertical pen's position.
-        penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender);
+        penY += -layout.descender + layout.lineSpacing + GetLineSpacing(layout.ascender + -layout.descender, layout.relativeLineSize);
 
         // Increase the glyph index.
         index = nextIndex;
index 35a1cc5..859547e 100644 (file)
@@ -34,7 +34,8 @@ namespace Text
 namespace
 {
 const std::string XHTML_ALIGN_ATTRIBUTE("align");
-}
+const std::string XHTML_RELATIVE_LINE_HEIGHT_ATTRIBUTE("rel-line-height");
+} // namespace
 
 void ProcessHorizontalAlignment(const Attribute& attribute, BoundedParagraphRun& boundedParagraphRun)
 {
@@ -43,6 +44,12 @@ void ProcessHorizontalAlignment(const Attribute& attribute, BoundedParagraphRun&
                                                                                             boundedParagraphRun.horizontalAlignment);
 }
 
+void ProcessRelativeLineHeight(const Attribute& attribute, BoundedParagraphRun& boundedParagraphRun)
+{
+  boundedParagraphRun.relativeLineSize        = StringToFloat(attribute.valueBuffer);
+  boundedParagraphRun.relativeLineSizeDefined = true;
+}
+
 void ProcessAttributesOfParagraphTag(const Tag& tag, BoundedParagraphRun& boundedParagraphRun)
 {
   // By default the align attribute is not defined until it's parsed.
@@ -58,9 +65,12 @@ void ProcessAttributesOfParagraphTag(const Tag& tag, BoundedParagraphRun& bounde
     {
       ProcessHorizontalAlignment(attribute, boundedParagraphRun);
     }
+    else if(TokenComparison(XHTML_RELATIVE_LINE_HEIGHT_ATTRIBUTE, attribute.nameBuffer, attribute.nameLength))
+    {
+      ProcessRelativeLineHeight(attribute, boundedParagraphRun);
+    }
   }
 }
-
 } // namespace Text
 
 } // namespace Toolkit
index 484b3a5..2859794 100644 (file)
@@ -44,6 +44,14 @@ void ProcessHorizontalAlignment(const Attribute& attribute, BoundedParagraphRun&
  */
 void ProcessAttributesOfParagraphTag(const Tag& tag, BoundedParagraphRun& boundedParagraphRun);
 
+/**
+ * @brief Retrieves the relative line height value from the paragraph tag and sets it to the bounded paragraph run.
+ *
+ * @param[in] attribute the relative line height attribute.
+ * @param[in,out] boundedParagraphRun The bounded paragraph run.
+ */
+void ProcessRelativeLineHeight(const Attribute& attribute, BoundedParagraphRun& boundedParagraphRun);
+
 } // namespace Text
 
 } // namespace Toolkit