sw/qa/uibase/shells/data/three-changes.fodt |    6 ++++-
 sw/qa/uibase/shells/shells.cxx              |   12 ++++++++---
 sw/source/uibase/uno/loktxdoc.cxx           |   30 ++++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 4 deletions(-)

New commits:
commit d6e4ffb0cc06e54b1d04441c8ca8f51161c873cf
Author:     Mike Kaganski <[email protected]>
AuthorDate: Fri Sep 19 12:18:39 2025 +0500
Commit:     Miklos Vajna <[email protected]>
CommitDate: Mon Sep 22 08:54:38 2025 +0200

    LOK Extract API: add startPageNumber argument to redline data extraction
    
    Allows to obtain page number of starting points of redlines, returned
    in new startPageNumber field.
    
    Since this information requires manipulations with XTextViewCursor,
    which is expensive, this is made optional, and is off by default. The
    argument must get "true" value to enable the functionality.
    
    Change-Id: I6180eb8789f44cd20d8da277452987df6ebce055
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191177
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sw/qa/uibase/shells/data/three-changes.fodt 
b/sw/qa/uibase/shells/data/three-changes.fodt
index 0c7072b244a0..ba13bcb58f41 100644
--- a/sw/qa/uibase/shells/data/three-changes.fodt
+++ b/sw/qa/uibase/shells/data/three-changes.fodt
@@ -2,6 +2,9 @@
 
 <office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:dc="http://purl.org/dc/elements/1.1/"; 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
  <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph">
+   <style:paragraph-properties fo:break-before="page"/>
+  </style:style>
   <style:style style:name="T1" style:family="text">
    <style:text-properties fo:font-weight="bold"/>
   </style:style>
@@ -34,7 +37,8 @@
      </text:insertion>
     </text:changed-region>
    </text:tracked-changes>
-   <text:p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum 
consequat mi quis pretium semper. Proin luctus orci ac neque venenatis, quis 
commodo dolor posuere. Curabitur dignissim sapien quis cursus egestas. 
<text:change-start text:change-id="ct2412392131792"/>Donec<text:change-end 
text:change-id="ct2412392131792"/> blandit auctor arcu, nec <text:change-start 
text:change-id="ct2412428113776"/><text:span 
text:style-name="T1">pellentesque</text:span><text:change-end 
text:change-id="ct2412428113776"/> eros molestie eget. In consectetur aliquam 
hendrerit. Sed cursus mauris vitae ligula pellentesque, non pellentesque urna 
aliquet. Fusce placerat mauris enim, nec rutrum purus semper vel. Praesent 
tincidunt neque eu pellentesque pharetra. Fusce pellentesque est 
orci.<text:change-start text:change-id="ct2412428117232"/> Sapienti 
sat.<text:change-end text:change-id="ct2412428117232"/></text:p>
+   <text:p/>
+   <text:p text:style-name="P1">Lorem ipsum dolor sit amet, consectetur 
adipiscing elit. Vestibulum consequat mi quis pretium semper. Proin luctus orci 
ac neque venenatis, quis commodo dolor posuere. Curabitur dignissim sapien quis 
cursus egestas. <text:change-start 
text:change-id="ct2412392131792"/>Donec<text:change-end 
text:change-id="ct2412392131792"/> blandit auctor arcu, nec <text:change-start 
text:change-id="ct2412428113776"/><text:span 
text:style-name="T1">pellentesque</text:span><text:change-end 
text:change-id="ct2412428113776"/> eros molestie eget. In consectetur aliquam 
hendrerit. Sed cursus mauris vitae ligula pellentesque, non pellentesque urna 
aliquet. Fusce placerat mauris enim, nec rutrum purus semper vel. Praesent 
tincidunt neque eu pellentesque pharetra. Fusce pellentesque est 
orci.<text:change-start text:change-id="ct2412428117232"/> Sapienti 
sat.<text:change-end text:change-id="ct2412428117232"/></text:p>
   </office:text>
  </office:body>
 </office:document>
\ No newline at end of file
diff --git a/sw/qa/uibase/shells/shells.cxx b/sw/qa/uibase/shells/shells.cxx
index 2780cce809ff..165eb7589379 100644
--- a/sw/qa/uibase/shells/shells.cxx
+++ b/sw/qa/uibase/shells/shells.cxx
@@ -921,6 +921,7 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testDocumentStructureExtractRedlines)
         CPPUNIT_ASSERT_EQUAL("Mike"s, change.get<std::string>("author"));
         CPPUNIT_ASSERT_EQUAL("Delete “Donec”"s, 
change.get<std::string>("description"));
         CPPUNIT_ASSERT_EQUAL(""s, change.get<std::string>("comment"));
+        CPPUNIT_ASSERT(bool(change.find("startPageNumber") == 
change.not_found()));
         auto text_before = change.get<std::string>("textBefore");
         CPPUNIT_ASSERT_EQUAL(size_t(200), text_before.size());
         CPPUNIT_ASSERT(text_before.ends_with(" egestas. "));
@@ -942,6 +943,7 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testDocumentStructureExtractRedlines)
         CPPUNIT_ASSERT_EQUAL("Mike"s, change.get<std::string>("author"));
         CPPUNIT_ASSERT_EQUAL("Attributes changed"s, 
change.get<std::string>("description"));
         CPPUNIT_ASSERT_EQUAL(""s, change.get<std::string>("comment"));
+        CPPUNIT_ASSERT(bool(change.find("startPageNumber") == 
change.not_found()));
         auto text_before = change.get<std::string>("textBefore");
         CPPUNIT_ASSERT_EQUAL(size_t(200), text_before.size());
         CPPUNIT_ASSERT(text_before.ends_with(" arcu, nec "));
@@ -963,6 +965,7 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testDocumentStructureExtractRedlines)
         CPPUNIT_ASSERT_EQUAL("Mike"s, change.get<std::string>("author"));
         CPPUNIT_ASSERT_EQUAL("Insert “ Sapienti sat.”"s, 
change.get<std::string>("description"));
         CPPUNIT_ASSERT_EQUAL(""s, change.get<std::string>("comment"));
+        CPPUNIT_ASSERT(bool(change.find("startPageNumber") == 
change.not_found()));
         auto text_before = change.get<std::string>("textBefore");
         CPPUNIT_ASSERT_EQUAL(size_t(200), text_before.size());
         CPPUNIT_ASSERT(text_before.ends_with(" est orci."));
@@ -974,12 +977,12 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testDocumentStructureExtractRedlines)
 
     CPPUNIT_ASSERT(bool(it == docStructure.end()));
 
-    // 2. Test contextLen filter argument
+    // 2. Test contextLen and startPageNumber filter arguments
     {
         // extract
         tools::JsonWriter aJsonWriter;
-        std::string_view aCommand(
-            ".uno:ExtractDocumentStructure?filter=trackchanges,foo:bar, 
contextLen: 15,,");
+        std::string_view 
aCommand(".uno:ExtractDocumentStructure?filter=trackchanges,foo:bar, "
+                                  "contextLen: 15, startPageNumber :true ,,");
         getSwTextDoc()->getCommandValues(aJsonWriter, aCommand);
 
         std::stringstream 
aStream(std::string(aJsonWriter.finishAndGetAsOString()));
@@ -996,6 +999,7 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testDocumentStructureExtractRedlines)
         CPPUNIT_ASSERT(it != docStructure.end());
         const auto & [ name, change ] = *it;
         CPPUNIT_ASSERT_EQUAL("TrackChanges.ByIndex.0"s, name);
+        CPPUNIT_ASSERT_EQUAL("2"s, change.get<std::string>("startPageNumber"));
         CPPUNIT_ASSERT_EQUAL("ursus egestas. "s, 
change.get<std::string>("textBefore"));
         CPPUNIT_ASSERT_EQUAL(" blandit auctor"s, 
change.get<std::string>("textAfter"));
         ++it;
@@ -1006,6 +1010,7 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testDocumentStructureExtractRedlines)
         CPPUNIT_ASSERT(it != docStructure.end());
         const auto & [ name, change ] = *it;
         CPPUNIT_ASSERT_EQUAL("TrackChanges.ByIndex.1"s, name);
+        CPPUNIT_ASSERT_EQUAL("2"s, change.get<std::string>("startPageNumber"));
         CPPUNIT_ASSERT_EQUAL("ctor arcu, nec "s, 
change.get<std::string>("textBefore"));
         CPPUNIT_ASSERT_EQUAL(" eros molestie "s, 
change.get<std::string>("textAfter"));
         ++it;
@@ -1016,6 +1021,7 @@ CPPUNIT_TEST_FIXTURE(SwUibaseShellsTest, 
testDocumentStructureExtractRedlines)
         CPPUNIT_ASSERT(it != docStructure.end());
         const auto & [ name, change ] = *it;
         CPPUNIT_ASSERT_EQUAL("TrackChanges.ByIndex.2"s, name);
+        CPPUNIT_ASSERT_EQUAL("2"s, change.get<std::string>("startPageNumber"));
         CPPUNIT_ASSERT_EQUAL("esque est orci."s, 
change.get<std::string>("textBefore"));
         CPPUNIT_ASSERT_EQUAL(""s, change.get<std::string>("textAfter"));
         ++it;
diff --git a/sw/source/uibase/uno/loktxdoc.cxx 
b/sw/source/uibase/uno/loktxdoc.cxx
index 4b6e2216bf07..f5539e48b39b 100644
--- a/sw/source/uibase/uno/loktxdoc.cxx
+++ b/sw/source/uibase/uno/loktxdoc.cxx
@@ -25,6 +25,7 @@
 
 #include <com/sun/star/beans/XPropertyAccess.hpp>
 
+#include <comphelper/diagnose_ex.hxx>
 #include <comphelper/sequence.hxx>
 #include <o3tl/string_view.hxx>
 #include <tools/json_writer.hxx>
@@ -51,7 +52,9 @@
 #include <pagefrm.hxx>
 #include <com/sun/star/text/XTextContent.hpp>
 
+#include <com/sun/star/text/XPageCursor.hpp>
 #include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
+#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
 #include <com/sun/star/chart2/XChartDocument.hpp>
 #include <com/sun/star/chart/XChartDocument.hpp>
@@ -956,6 +959,7 @@ void GetDocStructureTrackChanges(tools::JsonWriter& 
rJsonWriter, SwDocShell* pDo
     if (!filterArguments.empty() && !filterArguments.starts_with(u","))
         return; // not a correct filter
     sal_Int16 nContextLen = 200;
+    bool bPageNumbers = false;
     for (size_t paramPos = 1; paramPos < filterArguments.size();)
     {
         std::u16string_view param = o3tl::getToken(filterArguments, u',', 
paramPos);
@@ -968,11 +972,25 @@ void GetDocStructureTrackChanges(tools::JsonWriter& 
rJsonWriter, SwDocShell* pDo
             if (!value.empty())
                 nContextLen = o3tl::toInt32(value);
         }
+        else if (token == u"startPageNumber")
+        {
+            bPageNumbers = value == u"true";
+        }
         // else unknown filter argument (maybe from a newer API?) - ignore
     }
 
     SwDoc& rDoc = *pDocShell->GetDoc();
     const SwRedlineTable& rTable = 
rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+    uno::Reference<text::XTextCursor> xTextViewCursor;
+    uno::Reference<text::XPageCursor> xTextPageCursor;
+    if (bPageNumbers)
+    {
+        if (auto xSupplier = 
pDocShell->GetController().query<text::XTextViewCursorSupplier>())
+        {
+            xTextViewCursor.set(xSupplier->getViewCursor());
+            xTextPageCursor.set(xTextViewCursor, uno::UNO_QUERY);
+        }
+    }
 
     for (size_t i = 0; i < rTable.size(); ++i)
     {
@@ -1001,6 +1019,18 @@ void GetDocStructureTrackChanges(tools::JsonWriter& 
rJsonWriter, SwDocShell* pDo
             {
                 auto xCursor = 
xStart->getText()->createTextCursorByRange(xStart);
                 xCursor->goLeft(nContextLen, /*bExpand*/ true);
+                if (xTextPageCursor)
+                {
+                    try
+                    {
+                        xTextViewCursor->gotoRange(xStart, false);
+                        rJsonWriter.put("startPageNumber", 
xTextPageCursor->getPage());
+                    }
+                    catch (const uno::Exception&)
+                    {
+                        DBG_UNHANDLED_EXCEPTION("sw.ui");
+                    }
+                }
                 rJsonWriter.put("textBefore", xCursor->getString());
             }
             if (xEnd)

Reply via email to