Support paragraph tag <p> in markup 84/270384/16
authorssabah <s.sabah@samsung.com>
Wed, 26 Jan 2022 16:00:03 +0000 (18:00 +0200)
committerssabah <s.sabah@samsung.com>
Tue, 22 Feb 2022 13:31:04 +0000 (15:31 +0200)
Paragraph tag is used as block of text start & end with new-line (implicitly).

The paragraph tag is nedded to support styles on paragraph level in markup. They are attributes for paragraph tag like: Align, Direction (LTR, RTL), Wrap, etc.

Example:
Text:
"Before paragraph <p>first paragraph</p><p>second paragraph</p><p>new line \n third paragraph</p>After paragraph"

Output:
============================
| Before paragraph         |
| first paragraph          |
| second paragraph         |
| new line                 |
| third paragraph          |
| After paragraph          |
============================

How to apply it in TextEditor:
 textEditor.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
 textEditor.SetProperty(TextEditor::Property::TEXT, "text one <p>Paragraph two</p> text three <p>Paragraph four</p> text five");

How to apply it in TextLabel:
 textLabel.SetProperty(TextLabel::Property::MULTI_LINE, true);
 textLabel.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
 textLabel.SetProperty(TextLabel::Property::TEXT, "text one <p>Paragraph two</p> text three <p>Paragraph four</p> text five");

Change-Id: Icee044db488d82646758ccc59a23ce896a3e28c7

28 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-BoundedParagraph-Functions.cpp [new file with mode: 0644]
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-TextLabel-internal.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextEditor.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextField.cpp
automated-tests/src/dali-toolkit/utc-Dali-TextLabel.cpp
dali-toolkit/devel-api/text/text-utils-devel.cpp
dali-toolkit/internal/file.list
dali-toolkit/internal/text/bounded-paragraph-helper-functions.cpp [new file with mode: 0644]
dali-toolkit/internal/text/bounded-paragraph-helper-functions.h [new file with mode: 0644]
dali-toolkit/internal/text/bounded-paragraph-run.h [new file with mode: 0644]
dali-toolkit/internal/text/character-run.h
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/view-model.cpp
dali-toolkit/internal/text/rendering/view-model.h
dali-toolkit/internal/text/text-controller-text-updater.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/text-view-interface.h
dali-toolkit/internal/text/text-view.cpp
dali-toolkit/internal/text/text-view.h

index 6ea7182..198d183 100755 (executable)
@@ -9,6 +9,7 @@ SET(CAPI_LIB "dali-toolkit-internal")
 SET(TC_SOURCES
  utc-Dali-AddOns.cpp
  utc-Dali-BidirectionalSupport.cpp
+ utc-Dali-BoundedParagraph-Functions.cpp
  utc-Dali-ColorConversion.cpp
  utc-Dali-Control-internal.cpp
  utc-Dali-DebugRendering.cpp
index 065e5fd..e22071a 100644 (file)
@@ -115,7 +115,8 @@ void CreateTextModel(const std::string&                text,
                                       logicalModel->mAnchors,
                                       logicalModel->mUnderlinedCharacterRuns,
                                       logicalModel->mBackgroundColorRuns,
-                                      logicalModel->mStrikethroughCharacterRuns);
+                                      logicalModel->mStrikethroughCharacterRuns,
+                                      logicalModel->mBoundedParagraphRuns);
 
   Length         textSize = 0u;
   const uint8_t* utf8     = NULL;
diff --git a/automated-tests/src/dali-toolkit-internal/utc-Dali-BoundedParagraph-Functions.cpp b/automated-tests/src/dali-toolkit-internal/utc-Dali-BoundedParagraph-Functions.cpp
new file mode 100644 (file)
index 0000000..07be747
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ * 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 <stdlib.h>
+#include <iostream>
+
+#include <dali-toolkit-test-suite-utils.h>
+#include <dali-toolkit/dali-toolkit.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/bounded-paragraph-helper-functions.h>
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
+#include <dali-toolkit/internal/text/character-set-conversion.h>
+
+using namespace Dali;
+using namespace Toolkit;
+using namespace Text;
+
+struct BoundedParagraphData
+{
+  CharacterIndex characterIndex;
+  Length         numberOfCharacters;
+};
+
+struct TestCaseData
+{
+  std::string           description;                       ///< Description of the test.
+  std::string           text;                              ///< The text.
+  CharacterIndex        firstIndexOfRemovedCharacters;     ///< The first index of removed characters.
+  int                   numberOfRemovedCharacters;         ///< The number of removed characters.
+  Length                numberOfBoundedParagraphs;         ///< The number of bounded paragraphs before merging.
+  BoundedParagraphData* boundedParagraphs;                 ///< The bounded paragraphs info before merging.
+  Length                numberOfExpectedBoundedParagraphs; ///< The number of expected bounded paragraphs after merging.
+  BoundedParagraphData* expectedBoundedParagraphs;         ///< The expected bounded paragraphs info after merging.
+};
+
+void CreateBoundedParagraphRunsFromBoundedParagraphData(
+  Vector<BoundedParagraphRun>& boundedParagraphRuns,
+  const BoundedParagraphData*  boundedParagraphs,
+  const Length&                numberOfBoundedParagraphs)
+{
+  boundedParagraphRuns.Clear();
+
+  if(boundedParagraphs != nullptr)
+  {
+    for(Length index = 0u; index < numberOfBoundedParagraphs; index++)
+    {
+      BoundedParagraphRun boundedParagraphRun;
+      boundedParagraphRun.characterRun.characterIndex     = boundedParagraphs[index].characterIndex;
+      boundedParagraphRun.characterRun.numberOfCharacters = boundedParagraphs[index].numberOfCharacters;
+
+      boundedParagraphRuns.PushBack(boundedParagraphRun);
+    }
+  }
+}
+
+bool MergeBoundedParagraphRunsTest(TestCaseData testCase)
+{
+  // 1) Convert boundedParagraphs to vector of BoundedParagraphRun
+  Vector<BoundedParagraphRun> boundedParagraphRuns;
+  CreateBoundedParagraphRunsFromBoundedParagraphData(boundedParagraphRuns, testCase.boundedParagraphs, testCase.numberOfBoundedParagraphs);
+
+  // 2) Convert expectedBoundedParagraphs to vector of BoundedParagraphRun
+  Vector<BoundedParagraphRun> expectedBoundedParagraphRuns;
+  CreateBoundedParagraphRunsFromBoundedParagraphData(expectedBoundedParagraphRuns, testCase.expectedBoundedParagraphs, testCase.numberOfExpectedBoundedParagraphs);
+
+  // 3) Convert string text to vector of character utf32
+  Vector<Character> utf32Text;
+  utf32Text.Resize(testCase.text.size());
+  const uint32_t numberOfCharacters = (testCase.text.size() == 0) ? 0 : Utf8ToUtf32(reinterpret_cast<const uint8_t* const>(testCase.text.c_str()), testCase.text.size(), &utf32Text[0u]);
+  utf32Text.Resize(numberOfCharacters);
+
+  // 4) Call MergeBoundedParagraphRunsWhenRemoveCharacters
+  MergeBoundedParagraphRunsWhenRemoveCharacters(utf32Text, testCase.firstIndexOfRemovedCharacters, testCase.numberOfRemovedCharacters, boundedParagraphRuns);
+
+  // 5) Verify actual with expected
+  if(testCase.numberOfExpectedBoundedParagraphs != boundedParagraphRuns.Count())
+  {
+    std::cout << "  Different number of bounded paragraph runs after merging: " << boundedParagraphRuns.Count() << ", expected : " << testCase.numberOfExpectedBoundedParagraphs << std::endl;
+    return false;
+  }
+
+  for(unsigned int index = 0u; index < testCase.numberOfExpectedBoundedParagraphs; ++index)
+  {
+    if(expectedBoundedParagraphRuns[index].characterRun.characterIndex != boundedParagraphRuns[index].characterRun.characterIndex)
+    {
+      std::cout << "  Different bounded paragraph runs after merging, index : " << index << std::endl;
+      std::cout << "  Different characterIndex, actual: " << boundedParagraphRuns[index].characterRun.characterIndex
+                << ", expected : " << expectedBoundedParagraphRuns[index].characterRun.characterIndex << std::endl;
+      return false;
+    }
+
+    if(expectedBoundedParagraphRuns[index].characterRun.numberOfCharacters != boundedParagraphRuns[index].characterRun.numberOfCharacters)
+    {
+      std::cout << "  Different bounded paragraph runs after merging, index : " << index << std::endl;
+      std::cout << "  Different numberOfCharacters, actual: " << boundedParagraphRuns[index].characterRun.numberOfCharacters
+                << ", expected : " << expectedBoundedParagraphRuns[index].characterRun.numberOfCharacters << std::endl;
+      return false;
+    }
+  }
+
+  return true;
+}
+
+int UtcDaliMergeBoundedParagraphRunsWhenRemoveCharacters(void)
+{
+  tet_infoline(" UtcDaliMergeBoundedParagraphRunsWhenRemoveCharacters ");
+
+  BoundedParagraphData boundedParagraphs01[]         = {{10u, 14u}, {37u, 15u}};
+  BoundedParagraphData expectedBoundedParagraphs01[] = {{10u, 42u}};
+
+  BoundedParagraphData boundedParagraphs02[]         = {{10u, 14u}, {37u, 15u}};
+  BoundedParagraphData expectedBoundedParagraphs02[] = {{37u, 15u}};
+
+  BoundedParagraphData boundedParagraphs03[]         = {{10u, 14u}, {37u, 15u}};
+  BoundedParagraphData expectedBoundedParagraphs03[] = {{10u, 14u}, {37u, 20u}};
+
+  BoundedParagraphData boundedParagraphs04[]         = {{10u, 14u}, {37u, 15u}};
+  BoundedParagraphData expectedBoundedParagraphs04[] = {{10u, 14u}, {37u, 15u}};
+
+  BoundedParagraphData boundedParagraphs05[]         = {{10u, 14u}, {37u, 15u}};
+  BoundedParagraphData expectedBoundedParagraphs05[] = {{10u, 14u}, {37u, 15u}};
+
+  BoundedParagraphData boundedParagraphs06[]         = {{10u, 14u}, {37u, 15u}, {64u, 14u}};
+  BoundedParagraphData expectedBoundedParagraphs06[] = {{10u, 68u}};
+
+  TestCaseData testCases[] =
+    {
+      {
+
+        "test-case 01",
+        "text one \nParagraph two\n text three \nParagraph four\n text five",
+        20u,
+        -26,
+        2u,
+        boundedParagraphs01,
+        1u,
+        expectedBoundedParagraphs01
+
+      },
+
+      {
+
+        "test-case 02",
+        "text one \nParagraph two\n text three \nParagraph four\n text five",
+        5u,
+        -5,
+        2u,
+        boundedParagraphs02,
+        1u,
+        expectedBoundedParagraphs02
+
+      },
+
+      {
+
+        "test-case 03",
+        "text one \nParagraph two\n text three \nParagraph four\n text five",
+        47u,
+        -10,
+        2u,
+        boundedParagraphs03,
+        2u,
+        expectedBoundedParagraphs03
+
+      },
+
+      {
+
+        "test-case 04",
+        "text one \nParagraph two\n text three \nParagraph four\n text five",
+        10u,
+        -9,
+        2u,
+        boundedParagraphs04,
+        2u,
+        expectedBoundedParagraphs04
+
+      },
+
+      {
+
+        "test-case 05",
+        "text one \nParagraph two\n text three \nParagraph four\n text five",
+        25u,
+        -4,
+        2u,
+        boundedParagraphs05,
+        2u,
+        expectedBoundedParagraphs05
+
+      },
+
+      {
+
+        "test-case 06",
+        "text one \nParagraph two\n text three \nParagraph four\n text five \nParagraph six\n text seven",
+        10u,
+        -63,
+        3u,
+        boundedParagraphs06,
+        1u,
+        expectedBoundedParagraphs06
+
+      },
+
+    };
+
+  const unsigned int numberOfTests = 6u;
+
+  for(unsigned int index = 0u; index < numberOfTests; ++index)
+  {
+    ToolkitTestApplication application;
+    tet_infoline(testCases[index].description.c_str());
+
+    if(!MergeBoundedParagraphRunsTest(testCases[index]))
+    {
+      tet_result(TET_FAIL);
+    }
+  }
+
+  tet_result(TET_PASS);
+  END_TEST;
+}
\ No newline at end of file
index 67299b4..7817c02 100644 (file)
@@ -185,7 +185,8 @@ bool XHTMLEntityToUTF8Test(const XHTMLEntityToUTF8Data& data)
   Vector<UnderlinedCharacterRun>    underlinedCharacterRuns;
   Vector<ColorRun>                  backgroundColorRuns;
   Vector<StrikethroughCharacterRun> strikethroughCharacterRuns;
-  MarkupProcessData                 markupProcessData(colorRuns, fontRuns, items, anchors, underlinedCharacterRuns, backgroundColorRuns, strikethroughCharacterRuns);
+  Vector<BoundedParagraphRun>       boundedParagraphRuns;
+  MarkupProcessData                 markupProcessData(colorRuns, fontRuns, items, anchors, underlinedCharacterRuns, backgroundColorRuns, strikethroughCharacterRuns, boundedParagraphRuns);
   ProcessMarkupString(data.xHTMLEntityString, markupProcessData);
 
   for(Vector<EmbeddedItem>::Iterator it    = items.Begin(),
index 46abf15..9d78336 100644 (file)
@@ -637,4 +637,38 @@ int UtcDaliTextEditorMarkupStrikethroughNoEndTag(void)
   DALI_TEST_EQUALS(numberOfStrikethroughRuns, expectedNumberOfStrikethroughGlyphs, TEST_LOCATION);
 
   END_TEST;
+}
+
+int UtcDaliTextEditorMarkupParagraphTag(void)
+
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliTextEditorMarkupParagraphTag ");
+
+  TextEditor textEditor = TextEditor::New();
+  application.GetScene().Add(textEditor);
+
+  textEditor.SetProperty(TextEditor::Property::TEXT, "text one <p>Paragraph two</p> text three <p>Paragraph four</p> text five");
+  textEditor.SetProperty(TextEditor ::Property::ENABLE_MARKUP, true);
+
+  application.SendNotification();
+  application.Render();
+
+  uint32_t expectedNumberOfBoundedParagraphRuns = 2u;
+
+  Toolkit::Internal::TextEditor& textEditorImpl               = GetImpl(textEditor);
+  const Text::Length             numberOfBoundedParagraphRuns = textEditorImpl.GetTextController()->GetTextModel()->GetNumberOfBoundedParagraphRuns();
+  DALI_TEST_EQUALS(numberOfBoundedParagraphRuns, expectedNumberOfBoundedParagraphRuns, TEST_LOCATION);
+
+  const Vector<BoundedParagraphRun>& boundedParagraphRuns = textEditorImpl.GetTextController()->GetTextModel()->GetBoundedParagraphRuns();
+
+  //<p>Paragraph two</p>
+  DALI_TEST_EQUALS(boundedParagraphRuns[0u].characterRun.characterIndex, 10u, TEST_LOCATION);
+  DALI_TEST_EQUALS(boundedParagraphRuns[0u].characterRun.numberOfCharacters, 14u, TEST_LOCATION);
+
+  //<p>Paragraph four</p>
+  DALI_TEST_EQUALS(boundedParagraphRuns[1u].characterRun.characterIndex, 37u, TEST_LOCATION);
+  DALI_TEST_EQUALS(boundedParagraphRuns[1u].characterRun.numberOfCharacters, 15u, TEST_LOCATION);
+
+  END_TEST;
 }
\ No newline at end of file
index b1c380a..bec9416 100644 (file)
@@ -524,4 +524,40 @@ int UtcDaliTextLabelMarkupStrikethroughNoEndTag(void)
   DALI_TEST_EQUALS(numberOfStrikethroughRuns, expectedNumberOfStrikethroughGlyphs, TEST_LOCATION);
 
   END_TEST;
+}
+
+int UtcDaliTextLabelMarkupParagraphTag(void)
+
+{
+  ToolkitTestApplication application;
+
+  tet_infoline(" UtcDaliTextLabelMarkupParagraphTag ");
+  TextLabel textLabel = TextLabel::New();
+
+  application.GetScene().Add(textLabel);
+
+  textLabel.SetProperty(TextLabel::Property::TEXT, "text one <p>Paragraph two</p> text three <p>Paragraph four</p> text five");
+  textLabel.SetProperty(TextLabel ::Property::ENABLE_MARKUP, true);
+  textLabel.SetProperty(TextLabel::Property::MULTI_LINE, true);
+
+  application.SendNotification();
+  application.Render();
+
+  uint32_t expectedNumberOfBoundedParagraphRuns = 2u;
+
+  Toolkit::Internal::TextLabel& textLabelImpl                = GetImpl(textLabel);
+  const Text::Length            numberOfBoundedParagraphRuns = textLabelImpl.GetTextController()->GetTextModel()->GetNumberOfBoundedParagraphRuns();
+  DALI_TEST_EQUALS(numberOfBoundedParagraphRuns, expectedNumberOfBoundedParagraphRuns, TEST_LOCATION);
+
+  const Vector<BoundedParagraphRun>& boundedParagraphRuns = textLabelImpl.GetTextController()->GetTextModel()->GetBoundedParagraphRuns();
+
+  //<p>Paragraph two</p>
+  DALI_TEST_EQUALS(boundedParagraphRuns[0u].characterRun.characterIndex, 10u, TEST_LOCATION);
+  DALI_TEST_EQUALS(boundedParagraphRuns[0u].characterRun.numberOfCharacters, 14u, TEST_LOCATION);
+
+  //<p>Paragraph four</p>
+  DALI_TEST_EQUALS(boundedParagraphRuns[1u].characterRun.characterIndex, 37u, TEST_LOCATION);
+  DALI_TEST_EQUALS(boundedParagraphRuns[1u].characterRun.numberOfCharacters, 15u, TEST_LOCATION);
+
+  END_TEST;
 }
\ No newline at end of file
index acf34a6..b2e1f16 100644 (file)
@@ -5748,3 +5748,42 @@ int UtcDaliTextEditorCharacterSpacing(void)
 
   END_TEST;
 }
+
+int UtcDaliToolkitTexteditorParagraphTag(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTexteditorParagraphTag");
+  TextEditor editorNewlineSeparator = TextEditor::New();
+  TextEditor editorParagraphTag     = TextEditor::New();
+  DALI_TEST_CHECK(editorNewlineSeparator);
+  DALI_TEST_CHECK(editorParagraphTag);
+
+  application.GetScene().Add(editorNewlineSeparator);
+  application.GetScene().Add(editorParagraphTag);
+
+  //Same utterance uses new-line to split paragraphs should give similar results for paragraph tag.
+  editorNewlineSeparator.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+  editorNewlineSeparator.SetProperty(Actor::Property::SIZE, Vector2(100.f, 50.f));
+  editorNewlineSeparator.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  editorNewlineSeparator.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  editorNewlineSeparator.SetProperty(TextEditor::Property::TEXT, "test paragraph tag \ntest paragraph tag \ntest paragraph tag ");
+
+  editorParagraphTag.SetProperty(TextEditor::Property::ENABLE_MARKUP, true);
+  editorParagraphTag.SetProperty(Actor::Property::SIZE, Vector2(100.f, 50.f));
+  editorParagraphTag.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  editorParagraphTag.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  editorParagraphTag.SetProperty(TextEditor::Property::TEXT, "test paragraph tag <p>test paragraph tag </p>test paragraph tag ");
+
+  application.SendNotification();
+  application.Render();
+
+  Vector3 textNaturalSizeNewlineSeparator = editorNewlineSeparator.GetNaturalSize();
+  Vector3 textNaturalSizeParagraphTag     = editorParagraphTag.GetNaturalSize();
+
+  DALI_TEST_EQUALS(textNaturalSizeNewlineSeparator, textNaturalSizeParagraphTag, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
\ No newline at end of file
index 06612b8..060fc9a 100644 (file)
@@ -5280,4 +5280,43 @@ int UtcDaliToolkitTextFieldUnderlineTypesGeneration3(void)
   application.Render();
 
   END_TEST;
+}
+
+int UtcDaliToolkitTextfieldParagraphTag(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextfieldParagraphTag");
+  TextField fieldNewlineSeparator = TextField::New();
+  TextField fieldParagraphTag     = TextField::New();
+  DALI_TEST_CHECK(fieldNewlineSeparator);
+  DALI_TEST_CHECK(fieldParagraphTag);
+
+  application.GetScene().Add(fieldNewlineSeparator);
+  application.GetScene().Add(fieldParagraphTag);
+
+  //Same utterance uses new-line to split paragraphs should give similar results for paragraph tag.
+  fieldNewlineSeparator.SetProperty(TextField::Property::ENABLE_MARKUP, true);
+  fieldNewlineSeparator.SetProperty(Actor::Property::SIZE, Vector2(100.f, 50.f));
+  fieldNewlineSeparator.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  fieldNewlineSeparator.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  fieldNewlineSeparator.SetProperty(TextField::Property::TEXT, "test paragraph tag \ntest paragraph tag \ntest paragraph tag ");
+
+  fieldParagraphTag.SetProperty(TextField::Property::ENABLE_MARKUP, true);
+  fieldParagraphTag.SetProperty(Actor::Property::SIZE, Vector2(100.f, 50.f));
+  fieldParagraphTag.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  fieldParagraphTag.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  fieldParagraphTag.SetProperty(TextField::Property::TEXT, "test paragraph tag <p>test paragraph tag </p>test paragraph tag ");
+
+  application.SendNotification();
+  application.Render();
+
+  Vector3 textNaturalSizeNewlineSeparator = fieldNewlineSeparator.GetNaturalSize();
+  Vector3 textNaturalSizeParagraphTag     = fieldParagraphTag.GetNaturalSize();
+
+  DALI_TEST_EQUALS(textNaturalSizeNewlineSeparator, textNaturalSizeParagraphTag, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
 }
\ No newline at end of file
index 95d0689..ecc5f67 100644 (file)
@@ -2653,4 +2653,47 @@ int UtcDaliTextLabelCharacterSpacing(void)
   application.Render();
 
   END_TEST;
+}
+
+int UtcDaliToolkitTextlabelParagraphTag(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline(" UtcDaliToolkitTextlabelParagraphTag");
+  TextLabel labelNewlineSeparator = TextLabel::New();
+  TextLabel labelParagraphTag     = TextLabel::New();
+  DALI_TEST_CHECK(labelNewlineSeparator);
+  DALI_TEST_CHECK(labelParagraphTag);
+
+  application.GetScene().Add(labelNewlineSeparator);
+  application.GetScene().Add(labelParagraphTag);
+
+  //Same utterance uses new-line to split paragraphs should give similar results for paragraph tag.
+  labelNewlineSeparator.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  labelNewlineSeparator.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  labelNewlineSeparator.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+  labelNewlineSeparator.SetProperty(Actor::Property::SIZE, Vector2(100.f, 50.f));
+  labelNewlineSeparator.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  labelNewlineSeparator.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  labelNewlineSeparator.SetProperty(TextLabel::Property::TEXT, "test paragraph tag \ntest paragraph tag \ntest paragraph tag ");
+
+  labelParagraphTag.SetProperty(TextLabel::Property::MULTI_LINE, true);
+  labelParagraphTag.SetProperty(TextLabel::Property::ELLIPSIS, false);
+  labelParagraphTag.SetProperty(TextLabel::Property::ENABLE_MARKUP, true);
+  labelParagraphTag.SetProperty(Actor::Property::SIZE, Vector2(100.f, 50.f));
+  labelParagraphTag.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
+  labelParagraphTag.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  labelParagraphTag.SetProperty(TextLabel::Property::TEXT, "test paragraph tag <p>test paragraph tag </p>test paragraph tag ");
+
+  application.SendNotification();
+  application.Render();
+
+  Vector3 textNaturalSizeNewlineSeparator = labelNewlineSeparator.GetNaturalSize();
+  Vector3 textNaturalSizeParagraphTag     = labelParagraphTag.GetNaturalSize();
+
+  DALI_TEST_EQUALS(textNaturalSizeNewlineSeparator, textNaturalSizeParagraphTag, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
 }
\ No newline at end of file
index 7242687..81b82d8 100644 (file)
@@ -177,7 +177,8 @@ void ShapeTextPreprocess(const RendererParameters& textParameters, TextAbstracti
                                       textModel->mLogicalModel->mAnchors,
                                       textModel->mLogicalModel->mUnderlinedCharacterRuns,
                                       textModel->mLogicalModel->mBackgroundColorRuns,
-                                      textModel->mLogicalModel->mStrikethroughCharacterRuns);
+                                      textModel->mLogicalModel->mStrikethroughCharacterRuns,
+                                      textModel->mLogicalModel->mBoundedParagraphRuns);
 
   if(textParameters.markupEnabled)
   {
index c65b61e..9b543fd 100644 (file)
@@ -136,6 +136,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/image-loader/image-url-impl.cpp
    ${toolkit_src_dir}/styling/style-manager-impl.cpp
    ${toolkit_src_dir}/text/bidirectional-support.cpp
+   ${toolkit_src_dir}/text/bounded-paragraph-helper-functions.cpp
    ${toolkit_src_dir}/text/character-set-conversion.cpp
    ${toolkit_src_dir}/text/color-segmentation.cpp
    ${toolkit_src_dir}/text/cursor-helper-functions.cpp
diff --git a/dali-toolkit/internal/text/bounded-paragraph-helper-functions.cpp b/dali-toolkit/internal/text/bounded-paragraph-helper-functions.cpp
new file mode 100644 (file)
index 0000000..7b207f4
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ * 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.
+ *
+ */
+
+// FILE HEADER
+#include <dali-toolkit/internal/text/bounded-paragraph-helper-functions.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/script.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+void MergeBoundedParagraphRunsWhenRemoveCharacters(const Vector<Character>&     text,
+                                                   const CharacterIndex&        index,
+                                                   const int&                   numberOfCharacters,
+                                                   Vector<BoundedParagraphRun>& boundedParagraphRuns)
+{
+  // This works on boundedParagraphRuns before applying on them the changes of the removed characters.
+  // Update to merge only when characters have been removed.
+  if(numberOfCharacters >= 0)
+  {
+    return;
+  }
+
+  //No runs to merge
+  if(boundedParagraphRuns.Count() == 0u)
+  {
+    return;
+  }
+
+  const Length          numberOfRemovedCharacters     = (Length)(-1 * numberOfCharacters);
+  const Length          totalNumberOfCharacters       = text.Count();
+  const CharacterIndex& firstIndexOfRemovedCharacters = index;
+  const CharacterIndex  lastIndexOfRemovedCharacters  = ((firstIndexOfRemovedCharacters + numberOfRemovedCharacters) > 0u) ? (firstIndexOfRemovedCharacters + numberOfRemovedCharacters - 1u) : firstIndexOfRemovedCharacters; // Note: Length is uint32. Extra validation to avoid  a potential defects.
+
+  Length numberOfRuns          = boundedParagraphRuns.Count();
+  Length firstRunIndexToUpdate = 0;
+  Length lastRunIndexToUpdate  = 0;
+  bool   noNeedToMerge         = false;
+
+  // Find the first boundedParagraphRuns that is possible to be updated.
+  while(firstRunIndexToUpdate < numberOfRuns)
+  {
+    const CharacterIndex startCharIndex = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.characterIndex;
+    const CharacterIndex endCharIndex   = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.GetEndCharacterIndex();
+
+    // In-case the paragraph separator of the plain text (before the current bounded paragraph) is removed.
+    // In-case the start index of the current bounded paragraph is between start and end indices of removed characters.
+    // In-case the end index of the current bounded paragraph is between start and end indices of removed characters.
+    if((startCharIndex == lastIndexOfRemovedCharacters + 1u) ||
+       ((firstIndexOfRemovedCharacters <= startCharIndex) && (startCharIndex <= lastIndexOfRemovedCharacters)) ||
+       ((firstIndexOfRemovedCharacters <= endCharIndex) && (endCharIndex <= lastIndexOfRemovedCharacters)))
+    {
+      break;
+    }
+    else if(lastIndexOfRemovedCharacters + 1u < startCharIndex)
+    { // The whole removed characters are exist before the remaining bounded paragraphs.
+      noNeedToMerge = true;
+      break;
+    }
+    firstRunIndexToUpdate++;
+  }
+
+  // There is no run was affected by the removed characters.
+  if(noNeedToMerge || (firstRunIndexToUpdate == numberOfRuns))
+  {
+    return;
+  }
+
+  // Find the last boundedParagraphRuns that is possible to be updated.
+  lastRunIndexToUpdate = firstRunIndexToUpdate;
+  while(lastRunIndexToUpdate < numberOfRuns - 1u)
+  {
+    const CharacterIndex startCharIndex = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.characterIndex;
+    const CharacterIndex endCharIndex   = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.GetEndCharacterIndex();
+
+    if((lastIndexOfRemovedCharacters < endCharIndex) ||
+       (lastIndexOfRemovedCharacters + 1u <= startCharIndex))
+    {
+      break;
+    }
+
+    lastRunIndexToUpdate++;
+  }
+
+  // Remove all boundedParagraphRun between firstRunIndexToUpdate and lastRunIndexToUpdate
+  // At least one boundedParagraphRun between firstRunIndexToUpdate and lastRunIndexToUpdate
+  if(firstRunIndexToUpdate + 1u < lastRunIndexToUpdate)
+  {
+    Length runIndexToDelete = firstRunIndexToUpdate + 1u;
+
+    while(runIndexToDelete < lastRunIndexToUpdate)
+    {
+      Dali::Vector<BoundedParagraphRun>::Iterator paragraphToDelete = boundedParagraphRuns.Begin() + (runIndexToDelete);
+      boundedParagraphRuns.Remove(paragraphToDelete);
+
+      lastRunIndexToUpdate--;
+      numberOfRuns--;
+    }
+  }
+
+  CharacterIndex endCharIndexFirstRun = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.GetEndCharacterIndex();
+  ;
+  if(firstRunIndexToUpdate == lastRunIndexToUpdate)
+  {
+    if(endCharIndexFirstRun < lastIndexOfRemovedCharacters)
+    {
+      boundedParagraphRuns[firstRunIndexToUpdate].characterRun.numberOfCharacters += (lastIndexOfRemovedCharacters - endCharIndexFirstRun);
+    }
+  }
+  else
+  {
+    CharacterIndex startCharIndexLastRun = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.characterIndex;
+    boundedParagraphRuns[firstRunIndexToUpdate].characterRun.numberOfCharacters += ((startCharIndexLastRun - endCharIndexFirstRun) > 0u) ? (startCharIndexLastRun - endCharIndexFirstRun - 1u) : 0u; // Note: Length is uint32. Extra validation to avoid  a potential defects.
+
+    CharacterIndex endCharIndexLastRun = boundedParagraphRuns[lastRunIndexToUpdate].characterRun.GetEndCharacterIndex();
+
+    if(endCharIndexLastRun < lastIndexOfRemovedCharacters)
+    {
+      boundedParagraphRuns[lastRunIndexToUpdate].characterRun.numberOfCharacters += (lastIndexOfRemovedCharacters - endCharIndexLastRun);
+    }
+  }
+
+  // Each endCharIndex for boundedParagraphRun is a paragraph separator.
+  // If not then keep adding characters until find paragraph separator.
+  Length runIndex = firstRunIndexToUpdate;
+  while(runIndex <= lastRunIndexToUpdate)
+  {
+    CharacterIndex endCharIndex = boundedParagraphRuns[runIndex].characterRun.GetEndCharacterIndex();
+
+    // The remaining text was not affected.
+    if(endCharIndex > lastIndexOfRemovedCharacters)
+    {
+      break;
+    }
+
+    // Reference for numberOfCharacters in current run to update it
+    CharacterIndex& numberOfCharactersInRun = boundedParagraphRuns[runIndex].characterRun.numberOfCharacters;
+
+    // In-case arrived to the start of the next run and there is no paragraph separator.
+    // Merging the next run with the current run. Removing the next run.
+    if((runIndex + 1u < numberOfRuns) &&
+       (endCharIndex <= lastIndexOfRemovedCharacters) &&
+       ((lastIndexOfRemovedCharacters + 1u) < totalNumberOfCharacters) &&
+       (!TextAbstraction::IsNewParagraph(*(text.Begin() + lastIndexOfRemovedCharacters + 1u))))
+    {
+      numberOfCharactersInRun += boundedParagraphRuns[runIndex + 1u].characterRun.numberOfCharacters;
+
+      Dali::Vector<BoundedParagraphRun>::Iterator paragraphToDelete = boundedParagraphRuns.Begin() + (runIndex + 1u);
+      boundedParagraphRuns.Remove(paragraphToDelete);
+
+      numberOfRuns--;
+      lastRunIndexToUpdate--;
+
+      continue;
+    }
+
+    runIndex++;
+  }
+
+  // Each startCharIndex-1u for boundedParagraphRun is a paragraph separator.
+  // If not then remove boundedParagraphRun.
+  if(numberOfRuns > 0u &&
+     firstIndexOfRemovedCharacters > 0u &&
+     boundedParagraphRuns[firstRunIndexToUpdate].characterRun.characterIndex > 0u)
+  {
+    const CharacterIndex startCharIndex = boundedParagraphRuns[firstRunIndexToUpdate].characterRun.characterIndex;
+
+    if(firstIndexOfRemovedCharacters <= startCharIndex && startCharIndex <= lastIndexOfRemovedCharacters + 1u)
+    {
+      // Verify the paragraph separator before the first run to update
+      // In-case the paragraph separator is removed before the boundedParagraphRun.
+      // Remove the boundedParagraphRun.
+      if((!TextAbstraction::IsNewParagraph(*(text.Begin() + firstIndexOfRemovedCharacters - 1u))) &&
+         (!TextAbstraction::IsNewParagraph(*(text.Begin() + startCharIndex))))
+      {
+        Dali::Vector<BoundedParagraphRun>::Iterator paragraphToDelete = boundedParagraphRuns.Begin() + (firstRunIndexToUpdate);
+        boundedParagraphRuns.Remove(paragraphToDelete);
+      }
+    }
+  }
+}
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
\ No newline at end of file
diff --git a/dali-toolkit/internal/text/bounded-paragraph-helper-functions.h b/dali-toolkit/internal/text/bounded-paragraph-helper-functions.h
new file mode 100644 (file)
index 0000000..cdcbc69
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef DALI_TOOLKIT_TEXT_BOUNDED_PARAGRAPH_HELPER_FUNCTIONS_H
+#define DALI_TOOLKIT_TEXT_BOUNDED_PARAGRAPH_HELPER_FUNCTIONS_H
+
+/*
+ * 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.
+ * 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/common/dali-vector.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+/**
+ * @brief Merge BoundedParagraphRun when the paragraph separator has been removed.
+ * Adding the next characters until arrived to a paragraph separator.
+ * Or merging with the next BoundedParagraphRun and removing the next BoundedParagraphRun.
+ *
+ * @param[in] text Vector of UTF-32 characters.
+ * @param[in] index Index to the first character updated.
+ * @param[in] numberOfCharacters The number of the removed characters.
+ * @param[inout] boundedParagraphRuns The bounded paragraph runs.
+ *
+ */
+void MergeBoundedParagraphRunsWhenRemoveCharacters(const Vector<Character>&     text,
+                                                   const CharacterIndex&        index,
+                                                   const int&                   numberOfCharacters,
+                                                   Vector<BoundedParagraphRun>& boundedParagraphRuns);
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_BOUNDED_PARAGRAPH_HELPER_FUNCTIONS_H
diff --git a/dali-toolkit/internal/text/bounded-paragraph-run.h b/dali-toolkit/internal/text/bounded-paragraph-run.h
new file mode 100644 (file)
index 0000000..6c5b2d8
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef DALI_TOOLKIT_TEXT_BOUNDED_PARAGRAPH_RUN_H
+#define DALI_TOOLKIT_TEXT_BOUNDED_PARAGRAPH_RUN_H
+
+/*
+ * 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.
+ * 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/vector2.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/character-run.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Text
+{
+/**
+ * @brief BoundedParagraphRun
+ *
+ * In terms of the bidirectional algorithm, a 'paragraph' is understood as a run of characters between Paragraph Separators or appropriate Newline Functions.
+ * A 'paragraph' may also be determined by higher-level protocols like a mark-up tag.
+ * Bounded-paragraph is a pragraph that have been bounded by explicit tags like a mark-up tag.
+ * Bounded-paragraph could contain multi paragraphs that have been breaked by Paragraph Separators or appropriate Newline Functions.
+ * This will be used to handle information for the attributes of markup tag. Like TextAlign, TextDirection, TextIndent, LineHeight, etc.
+ */
+struct BoundedParagraphRun
+{
+  CharacterRun characterRun; ///< The initial character index within the whole text and the number of characters of the run.
+};
+
+} // namespace Text
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_TEXT_BOUNDED_PARAGRAPH_RUN_H
index 51a3ef7..757beb0 100644 (file)
@@ -46,6 +46,21 @@ struct CharacterRun
 
   CharacterIndex characterIndex;     ///< Index to the first character.
   Length         numberOfCharacters; ///< Number of characters in the run.
+
+  //Methods
+
+  /**
+  * @brief Calculate the end index in run.
+  * @return the end character index in run.
+  */
+  CharacterIndex GetEndCharacterIndex() const
+  {
+    DALI_ASSERT_DEBUG(!((0u == numberOfCharacters) && (characterIndex > 0u)) &&
+                      "Toolkit::Text::CharacterRun. NumberOfCharacters should be greater than zero");
+
+    // Note: Length is uint32. Extra validation to avoid a potential defects.
+    return (numberOfCharacters == 0u ? 0u : (characterIndex + numberOfCharacters - 1u));
+  }
 };
 
 } // namespace Text
index 4516662..9ad00bc 100644 (file)
@@ -19,6 +19,7 @@
 #include <dali-toolkit/internal/text/logical-model-impl.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/bounded-paragraph-helper-functions.h>
 #include <dali-toolkit/internal/text/input-style.h>
 #include <dali-toolkit/internal/text/text-run-container.h>
 
@@ -331,6 +332,19 @@ void LogicalModel::UpdateTextStyleRuns(CharacterIndex index, int numberOfCharact
 
   // Free memory allocated for the font family name.
   FreeFontFamilyNames(removedFontDescriptionRuns);
+
+  // Process the bounded paragraph runs
+  MergeBoundedParagraphRunsWhenRemoveCharacters(mText,
+                                                index,
+                                                numberOfCharacters,
+                                                mBoundedParagraphRuns);
+
+  Vector<BoundedParagraphRun> removedBoundedParagraphRuns;
+  UpdateCharacterRuns<BoundedParagraphRun>(index,
+                                           numberOfCharacters,
+                                           totalNumberOfCharacters,
+                                           mBoundedParagraphRuns,
+                                           removedBoundedParagraphRuns);
 }
 
 void LogicalModel::RetrieveStyle(CharacterIndex index, InputStyle& style)
@@ -600,6 +614,16 @@ void LogicalModel::FindParagraphs(CharacterIndex             index,
   }
 }
 
+Length LogicalModel::GetNumberOfBoundedParagraphRuns() const
+{
+  return mBoundedParagraphRuns.Count();
+}
+
+const Vector<BoundedParagraphRun>& LogicalModel::GetBoundedParagraphRuns() const
+{
+  return mBoundedParagraphRuns;
+}
+
 void LogicalModel::ClearEmbeddedImages()
 {
   FreeEmbeddedItems(mEmbeddedItems);
index 08c636d..c54c523 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali-toolkit/internal/text/anchor.h>
 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
 #include <dali-toolkit/internal/text/bidirectional-paragraph-info-run.h>
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #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>
@@ -182,6 +183,20 @@ public:
                       Length                     numberOfCharacters,
                       Vector<ParagraphRunIndex>& paragraphs);
 
+  /**
+   * @brief Retrieves the number of bounded paragraph runs.
+   *
+   * @return The number of bounded paragraph runs.
+   */
+  Length GetNumberOfBoundedParagraphRuns() const;
+
+  /**
+   * @brief Retrieves the reference for bounded paragraph runs.
+   *
+   * @return The reference for bounded paragraph runs.
+   */
+  const Vector<BoundedParagraphRun>& GetBoundedParagraphRuns() const;
+
   // Embedded images
 
   /**
@@ -228,6 +243,7 @@ public:
   Vector<Anchor>                        mAnchors;
   Vector<UnderlinedCharacterRun>        mUnderlinedCharacterRuns;    ///< The underlined character run from markup-processor
   Vector<StrikethroughCharacterRun>     mStrikethroughCharacterRuns; ///< The strikethrough character run from markup-processor
+  Vector<BoundedParagraphRun>           mBoundedParagraphRuns;       ///< The bounded paragraph is used to handle a paragraph mark-up tag and it's attributes. Like TextAlign, TextDirection, TextIndent, LineHeight, etc.
 
   BidirectionalLineRunIndex mBidirectionalLineIndex; ///< The last fetched bidirectional line info.
 };
index 17aae96..e046049 100644 (file)
@@ -60,6 +60,7 @@ const std::string XHTML_ANCHOR_TAG("a");
 const std::string XHTML_BACKGROUND_TAG("background");
 const std::string XHTML_SPAN_TAG("span");
 const std::string XHTML_STRIKETHROUGH_TAG("s");
+const std::string XHTML_PARAGRAPH_TAG("p");
 
 const char LESS_THAN      = '<';
 const char GREATER_THAN   = '>';
@@ -74,6 +75,7 @@ const char CHAR_ARRAY_END = '\0';
 const char HEX_CODE       = 'x';
 
 const char WHITE_SPACE = 0x20; // ASCII value of the white space.
+const char NEW_LINE    = 0x0A; // ASCII value of the newline.
 
 // Range 1 0x0u < XHTML_DECIMAL_ENTITY_RANGE <= 0xD7FFu
 // Range 2 0xE000u < XHTML_DECIMAL_ENTITY_RANGE <= 0xFFFDu
@@ -211,6 +213,17 @@ void Initialize(StrikethroughCharacterRun& strikethroughCharacterRun)
 }
 
 /**
+ * @brief Initializes a  bounded-paragraph character run to its defaults.
+ *
+ * @param[in,out] boundedParagraphRun The bounded paragraphRun run to initialize.
+ */
+void Initialize(BoundedParagraphRun& boundedParagraphRun)
+{
+  boundedParagraphRun.characterRun.characterIndex     = 0u;
+  boundedParagraphRun.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.
@@ -637,6 +650,30 @@ void ProcessItemTag(
 }
 
 /**
+ * @brief Processes the paragraph-tag
+ *
+ * @param[in/out] markupProcessData The markup process data
+ * @param[in] tag The current tag
+ * @param[in] isEndBuffer Whether the end of buffer
+ * @param[in/out] characterIndex The current character index
+ */
+void ProcessParagraphTag(
+  MarkupProcessData& markupProcessData,
+  const Tag          tag,
+  bool               isEndBuffer,
+  CharacterIndex&    characterIndex)
+{
+  if((characterIndex > 0 &&
+      markupProcessData.markupProcessedText[characterIndex - 1u] != NEW_LINE) &&
+     (!(tag.isEndTag && isEndBuffer)))
+  {
+    // Insert new-line character at the start and end of paragraph.
+    markupProcessData.markupProcessedText.append(1u, NEW_LINE);
+    ++characterIndex;
+  }
+}
+
+/**
  * @brief Processes the anchor tag
  *
  * @param[in/out] markupProcessData The markup process data
@@ -764,13 +801,21 @@ void ProcessSpanForRun(
  * @param[in] colorRunIndex The color run index
  * @param[in] underlinedCharacterRunIndex The underlined character run index
  * @param[in] backgroundRunIndex The background run index
+ * @param[in] boundedParagraphRunIndex The bounded paragraph run index
+ *
  */
-void ResizeModelVectors(MarkupProcessData& markupProcessData, const RunIndex fontRunIndex, const RunIndex colorRunIndex, const RunIndex underlinedCharacterRunIndex, const RunIndex backgroundRunIndex)
+void ResizeModelVectors(MarkupProcessData& markupProcessData,
+                        const RunIndex     fontRunIndex,
+                        const RunIndex     colorRunIndex,
+                        const RunIndex     underlinedCharacterRunIndex,
+                        const RunIndex     backgroundRunIndex,
+                        const RunIndex     boundedParagraphRunIndex)
 {
   markupProcessData.fontRuns.Resize(fontRunIndex);
   markupProcessData.colorRuns.Resize(colorRunIndex);
   markupProcessData.underlinedCharacterRuns.Resize(underlinedCharacterRunIndex);
   markupProcessData.backgroundColorRuns.Resize(backgroundRunIndex);
+  markupProcessData.boundedParagraphRuns.Resize(boundedParagraphRunIndex);
 
 #ifdef DEBUG_ENABLED
   for(unsigned int i = 0; i < colorRunIndex; ++i)
@@ -895,6 +940,7 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
   RunIndex underlinedCharacterRunIndex    = 0u;
   RunIndex backgroundRunIndex             = 0u;
   RunIndex strikethroughCharacterRunIndex = 0u;
+  RunIndex boundedParagraphRunIndex       = 0u;
 
   // check tag reference
   int colorTagReference      = 0u;
@@ -905,6 +951,7 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
   int backgroundTagReference = 0u;
   int spanTagReference       = 0u;
   int sTagReference          = 0u;
+  int pTagReference          = 0u;
 
   // Give an initial default value to the model's vectors.
   markupProcessData.colorRuns.Reserve(DEFAULT_VECTOR_SIZE);
@@ -1001,6 +1048,12 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
         ProcessTagForRun<StrikethroughCharacterRun>(
           markupProcessData.strikethroughCharacterRuns, styleStack, tag, characterIndex, strikethroughCharacterRunIndex, sTagReference, [](const Tag& tag, StrikethroughCharacterRun& run) { ProcessStrikethroughTag(tag, run); });
       } // <s></s>
+      else if(TokenComparison(XHTML_PARAGRAPH_TAG, tag.buffer, tag.length))
+      {
+        ProcessParagraphTag(markupProcessData, tag, (markupStringBuffer == markupStringEndBuffer), characterIndex);
+        ProcessTagForRun<BoundedParagraphRun>(
+          markupProcessData.boundedParagraphRuns, styleStack, tag, characterIndex, boundedParagraphRunIndex, pTagReference, [](const Tag& tag, BoundedParagraphRun& run) {});
+      } // <p></p>
     }   // end if( IsTag() )
     else if(markupStringBuffer < markupStringEndBuffer)
     {
@@ -1009,7 +1062,7 @@ void ProcessMarkupString(const std::string& markupString, MarkupProcessData& mar
   }
 
   // Resize the model's vectors.
-  ResizeModelVectors(markupProcessData, fontRunIndex, colorRunIndex, underlinedCharacterRunIndex, backgroundRunIndex);
+  ResizeModelVectors(markupProcessData, fontRunIndex, colorRunIndex, underlinedCharacterRunIndex, backgroundRunIndex, boundedParagraphRunIndex);
 }
 
 } // namespace Text
index bb1b6ba..3ef7a2d 100644 (file)
@@ -24,6 +24,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/text/anchor.h>
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #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>
@@ -47,7 +48,8 @@ struct MarkupProcessData
                     Vector<Anchor>&                    anchors,
                     Vector<UnderlinedCharacterRun>&    underlinedCharacterRuns,
                     Vector<ColorRun>&                  backgroundColorRuns,
-                    Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns)
+                    Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns,
+                    Vector<BoundedParagraphRun>&       boundedParagraphRuns)
   : colorRuns(colorRuns),
     fontRuns(fontRuns),
     items(items),
@@ -55,6 +57,7 @@ struct MarkupProcessData
     underlinedCharacterRuns(underlinedCharacterRuns),
     backgroundColorRuns(backgroundColorRuns),
     strikethroughCharacterRuns(strikethroughCharacterRuns),
+    boundedParagraphRuns(boundedParagraphRuns),
     markupProcessedText()
   {
   }
@@ -66,6 +69,7 @@ struct MarkupProcessData
   Vector<UnderlinedCharacterRun>&    underlinedCharacterRuns;    ///< The underlined character runs.
   Vector<ColorRun>&                  backgroundColorRuns;        ///< The background color runs.
   Vector<StrikethroughCharacterRun>& strikethroughCharacterRuns; ///< The strikethrough character runs.
+  Vector<BoundedParagraphRun>&       boundedParagraphRuns;       ///< The bounded paragraph runs
   std::string                        markupProcessedText;        ///< The mark-up string.
 };
 
index 47aed00..0b26e4e 100644 (file)
@@ -645,6 +645,16 @@ void ViewModel::GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns, S
   mModel->GetStrikethroughRuns(strikethroughRuns, index, numberOfRuns);
 }
 
+Length ViewModel::GetNumberOfBoundedParagraphRuns() const
+{
+  return mModel->GetNumberOfBoundedParagraphRuns();
+}
+
+const Vector<BoundedParagraphRun>& ViewModel::GetBoundedParagraphRuns() const
+{
+  return mModel->GetBoundedParagraphRuns();
+}
+
 } // namespace Text
 
 } // namespace Toolkit
index ef8ba87..73b7fa6 100644 (file)
@@ -23,6 +23,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #include <dali-toolkit/internal/text/text-model-interface.h>
 #include <dali-toolkit/public-api/text/text-enumerations.h>
 
@@ -314,6 +315,16 @@ public:
   Length GetNumberOfStrikethroughRuns() const override;
 
   /**
+   * @copydoc ModelInterface::GetNumberOfBoundedParagraphRuns()
+   */
+  virtual Length GetNumberOfBoundedParagraphRuns() const override;
+
+  /**
+   * @copydoc ModelInterface::GetBoundedParagraphRuns()
+   */
+  virtual const Vector<BoundedParagraphRun>& GetBoundedParagraphRuns() const override;
+
+  /**
    * @copydoc ModelInterface::GetStrikethroughRuns()
    */
   void GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns, StrikethroughRunIndex index, Length numberOfRuns) const override;
index caf44f7..0b24bc0 100644 (file)
@@ -91,7 +91,8 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string&
                                         logicalModel->mAnchors,
                                         logicalModel->mUnderlinedCharacterRuns,
                                         logicalModel->mBackgroundColorRuns,
-                                        logicalModel->mStrikethroughCharacterRuns);
+                                        logicalModel->mStrikethroughCharacterRuns,
+                                        logicalModel->mBoundedParagraphRuns);
 
     Length         textSize = 0u;
     const uint8_t* utf8     = NULL;
index f9d993a..c7e7b98 100644 (file)
@@ -23,6 +23,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #include <dali-toolkit/internal/text/line-run.h>
 #include <dali-toolkit/internal/text/script-run.h>
 #include <dali-toolkit/internal/text/strikethrough-glyph-run.h>
@@ -388,6 +389,20 @@ public:
   virtual Length GetNumberOfStrikethroughRuns() const = 0;
 
   /**
+   * @brief Retrieves the number of bounded paragraph runs.
+   *
+   * @return The number of bounded paragraph runs.
+   */
+  virtual Length GetNumberOfBoundedParagraphRuns() const = 0;
+
+  /**
+   * @brief Retrieves the reference for bounded paragraph runs.
+   *
+   * @return The reference for bounded paragraph runs.
+   */
+  virtual const Vector<BoundedParagraphRun>& GetBoundedParagraphRuns() const = 0;
+
+  /**
    * @brief Retrieves the strikethrough runs.
    *
    * @param[out] strikethroughRuns Pointer to a buffer where the strikethrough runs are copied.
index 7ce74ca..924343c 100644 (file)
@@ -276,6 +276,16 @@ Length Model::GetNumberOfStrikethroughRuns() const
   return mVisualModel->GetNumberOfStrikethroughRuns();
 }
 
+Length Model::GetNumberOfBoundedParagraphRuns() const
+{
+  return mLogicalModel->GetNumberOfBoundedParagraphRuns();
+}
+
+const Vector<BoundedParagraphRun>& Model::GetBoundedParagraphRuns() const
+{
+  return mLogicalModel->GetBoundedParagraphRuns();
+}
+
 void Model::GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns, StrikethroughRunIndex index, Length numberOfRuns) const
 {
   mVisualModel->GetStrikethroughRuns(strikethroughRuns, index, numberOfRuns);
index 7077c85..ded9496 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/public-api/object/ref-object.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #include <dali-toolkit/internal/text/logical-model-impl.h>
 #include <dali-toolkit/internal/text/text-model-interface.h>
 #include <dali-toolkit/internal/text/visual-model-impl.h>
@@ -302,6 +303,16 @@ public:
    */
   void GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns, StrikethroughRunIndex index, Length numberOfRuns) const override;
 
+  /**
+   * @copydoc ModelInterface::GetNumberOfBoundedParagraphRuns()
+   */
+  virtual Length GetNumberOfBoundedParagraphRuns() const override;
+
+  /**
+   * @copydoc ModelInterface::GetBoundedParagraphRuns()
+   */
+  virtual const Vector<BoundedParagraphRun>& GetBoundedParagraphRuns() const override;
+
 private: // Private contructors & copy operator.
   /**
    * @brief Private constructor.
index 2dda239..9caf068 100644 (file)
@@ -23,6 +23,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/text/text-enumerations-devel.h>
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #include <dali-toolkit/internal/text/text-definitions.h>
 #include <dali-toolkit/internal/text/underlined-glyph-run.h>
 #include <dali-toolkit/public-api/text/text-enumerations.h>
@@ -323,6 +324,20 @@ public:
   virtual Length GetNumberOfStrikethroughRuns() const = 0;
 
   /**
+   * @brief Retrieves the number of bounded paragraph runs.
+   *
+   * @return The number of bounded paragraph runs.
+   */
+  virtual Length GetNumberOfBoundedParagraphRuns() const = 0;
+
+  /**
+   * @brief Retrieves the reference for bounded paragraph runs.
+   *
+   * @return The reference for bounded paragraph runs.
+   */
+  virtual const Vector<BoundedParagraphRun>& GetBoundedParagraphRuns() const = 0;
+
+  /**
    * @brief Retrieves the strikethrough runs.
    *
    * @param[out] strikethroughRuns Pointer to a buffer where the strikethrough runs are copied.
index 137ded2..ed64520 100644 (file)
@@ -824,6 +824,21 @@ void View::GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns,
   }
 }
 
+Length View::GetNumberOfBoundedParagraphRuns() const
+{
+  if(mImpl->mLogicalModel)
+  {
+    return mImpl->mLogicalModel->GetNumberOfBoundedParagraphRuns();
+  }
+
+  return 0u;
+}
+
+const Vector<BoundedParagraphRun>& View::GetBoundedParagraphRuns() const
+{
+  return mImpl->mLogicalModel->GetBoundedParagraphRuns();
+}
+
 const float View::GetCharacterSpacing() const
 {
   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetCharacterSpacing() : 0.f;
index 8eeb882..06bf838 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/text/bounded-paragraph-run.h>
 #include <dali-toolkit/internal/text/logical-model-impl.h>
 #include <dali-toolkit/internal/text/text-view-interface.h>
 #include <dali-toolkit/internal/text/visual-model-impl.h>
@@ -248,6 +249,16 @@ public:
                             Length                 numberOfRuns) const;
 
   /**
+   * @copydoc Dali::Toolkit::Text::ViewInterface::GetNumberOfBoundedParagraphRuns()
+   */
+  virtual Length GetNumberOfBoundedParagraphRuns() const;
+
+  /**
+   * @copydoc Dali::Toolkit::Text::ViewInterface::GetBoundedParagraphRuns()
+   */
+  virtual const Vector<BoundedParagraphRun>& GetBoundedParagraphRuns() const;
+
+  /**
    * @copydoc Dali::Toolkit::Text::ViewInterface::GetCharacterSpacing()
    */
   const float GetCharacterSpacing() const override;