[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.2' - 15 commits - include/svx include/vcl sd/CppunitTest_sd_tiledrendering.mk sd/inc sd/qa sd/source svx/source vcl/qa vcl/source

Tomaž Vajngerl (via logerrit) logerrit at kemper.freedesktop.org
Thu Jul 30 13:12:39 UTC 2020


 include/svx/svdmrkv.hxx                        |   10 
 include/vcl/VectorGraphicSearch.hxx            |   13 
 sd/CppunitTest_sd_tiledrendering.mk            |    1 
 sd/inc/Outliner.hxx                            |   11 
 sd/qa/unit/sdmodeltestbase.hxx                 |   18 +
 sd/qa/unit/tiledrendering/CallbackRecorder.hxx |  145 +++++++++
 sd/qa/unit/tiledrendering/LOKitSearchTest.cxx  |  322 ++++++++++++++++++++
 sd/qa/unit/tiledrendering/data/PDFSearch.pdf   |binary
 sd/qa/unit/tiledrendering/tiledrendering.cxx   |  140 --------
 sd/source/ui/view/Outliner.cxx                 |  394 ++++++++++++++++++-------
 svx/source/svdraw/svdmrkv.cxx                  |   48 ++-
 vcl/qa/cppunit/VectorGraphicSearchTest.cxx     |   79 +++++
 vcl/source/graphic/VectorGraphicSearch.cxx     |   56 ++-
 13 files changed, 986 insertions(+), 251 deletions(-)

New commits:
commit 8a6b1e3aed292b1ef594cfe97d55f99f7ca13632
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sun May 31 09:51:33 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 15:08:17 2020 +0200

    vcl: VectorGraphicSearch - clean-up SearchContext member vars.
    
    Change-Id: I1740be5ed1b47235da3794fa46e3533b17ca1fb8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95390
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 40d682542f02d78b5ed6bd4fc0ba461a1a7fb5f1)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95932
    (cherry picked from commit c1a973678474c9775730ae577ec349b76ff356bb)

diff --git a/vcl/source/graphic/VectorGraphicSearch.cxx b/vcl/source/graphic/VectorGraphicSearch.cxx
index 42d98a981ef9..94d954516f13 100644
--- a/vcl/source/graphic/VectorGraphicSearch.cxx
+++ b/vcl/source/graphic/VectorGraphicSearch.cxx
@@ -34,26 +34,26 @@ public:
 
 class SearchContext
 {
-public:
-    bool bInitialized = false;
-
+private:
     FPDF_DOCUMENT mpPdfDocument;
-    sal_Int32 mnPageIndex;
     FPDF_PAGE mpPage;
     FPDF_TEXTPAGE mpTextPage;
+    FPDF_SCHHANDLE mpSearchHandle;
+
+public:
+    sal_Int32 mnPageIndex;
     OUString maSearchString;
     SearchStartPosition meStartPosition;
-    FPDF_SCHHANDLE mpSearchHandle;
 
     SearchContext(FPDF_DOCUMENT pPdfDocument, sal_Int32 nPageIndex, OUString const& rSearchString,
                   SearchStartPosition eStartPosition)
         : mpPdfDocument(pPdfDocument)
-        , mnPageIndex(nPageIndex)
         , mpPage(nullptr)
         , mpTextPage(nullptr)
+        , mpSearchHandle(nullptr)
+        , mnPageIndex(nPageIndex)
         , maSearchString(rSearchString)
         , meStartPosition(eStartPosition)
-        , mpSearchHandle(nullptr)
     {
     }
 
commit a081f9d0cbbc003479c650fcad2e2415b2893403
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sat May 30 13:10:04 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 15:07:28 2020 +0200

    sd: improve the test for LOKit search in PDF graphic
    
    Record how many times we het the search result back, so we can
    be sure that the search happend and don't just read the old
    values. Assert the search result selection rectangles and text
    selection rectangles.
    
    Add tools:Rectangle support for CPPUnit into sdmodeltestbase.hxx
    
    Change-Id: I0b22d2d2f66abdc0dd0d5ac13a1bfebaa470749a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95383
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 4dcc48f37248a1eb45188739de961689e7873f3c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95931
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 60ec74e442aebe1d80b1416ec1405fffc06ab4ae)

diff --git a/sd/qa/unit/sdmodeltestbase.hxx b/sd/qa/unit/sdmodeltestbase.hxx
index b8231922e4cb..ad8defc88fe4 100644
--- a/sd/qa/unit/sdmodeltestbase.hxx
+++ b/sd/qa/unit/sdmodeltestbase.hxx
@@ -485,6 +485,22 @@ template<> struct assertion_traits<Color>
     }
 };
 
+template<> struct assertion_traits<tools::Rectangle>
+{
+    static bool equal( const tools::Rectangle& r1, const tools::Rectangle& r2 )
+    {
+        return r1 == r2;
+    }
+
+    static std::string toString( const tools::Rectangle& r)
+    {
+        OStringStream ost;
+        ost << "Rect P: [" << r.Top() << ", " << r.Left() << "] "
+            "S: [" << r.GetWidth() << ", " << r.GetHeight() << "]";
+        return ost.str();
+    }
+};
+
 CPPUNIT_NS_END
 
 #endif
diff --git a/sd/qa/unit/tiledrendering/CallbackRecorder.hxx b/sd/qa/unit/tiledrendering/CallbackRecorder.hxx
index fc5117cce6dc..7e6c8a42d07d 100644
--- a/sd/qa/unit/tiledrendering/CallbackRecorder.hxx
+++ b/sd/qa/unit/tiledrendering/CallbackRecorder.hxx
@@ -51,6 +51,7 @@ struct CallbackRecorder
         , m_nPart(0)
         , m_nSelectionBeforeSearchResult(0)
         , m_nSelectionAfterSearchResult(0)
+        , m_nSearchResultCount(0)
     {
     }
 
@@ -62,6 +63,7 @@ struct CallbackRecorder
     std::vector<int> m_aSearchResultPart;
     int m_nSelectionBeforeSearchResult;
     int m_nSelectionAfterSearchResult;
+    int m_nSearchResultCount;
     /// For document size changed callback.
     osl::Condition m_aDocumentSizeCondition;
 
@@ -115,6 +117,7 @@ struct CallbackRecorder
             break;
             case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
             {
+                m_nSearchResultCount++;
                 m_aSearchResultSelection.clear();
                 m_aSearchResultPart.clear();
                 boost::property_tree::ptree aTree;
diff --git a/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
index 33257f12d4ab..8d8b11e43fa1 100644
--- a/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
+++ b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
@@ -292,21 +292,29 @@ void LOKitSearchTest::testSearchInPDF()
     CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf,
                          pVectorGraphicData->getVectorGraphicDataType());
 
+    // Search
     lcl_search("ABC");
 
     CPPUNIT_ASSERT_EQUAL(true, mpCallbackRecorder->m_bFound);
+    CPPUNIT_ASSERT_EQUAL(1, mpCallbackRecorder->m_nSearchResultCount);
 
     CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultSelection.size());
+    CPPUNIT_ASSERT_EQUAL(OString("3763, 1331, 1432, 483"),
+                         mpCallbackRecorder->m_aSearchResultSelection[0]);
+    CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(3763, 1331), Size(1433, 484)),
+                         mpCallbackRecorder->m_aSelection[0]);
 
-    CPPUNIT_ASSERT_EQUAL(long(3763), mpCallbackRecorder->m_aSelection[0].Left());
-    CPPUNIT_ASSERT_EQUAL(long(1331), mpCallbackRecorder->m_aSelection[0].Top());
-    CPPUNIT_ASSERT_EQUAL(long(1433), mpCallbackRecorder->m_aSelection[0].GetWidth());
-    CPPUNIT_ASSERT_EQUAL(long(484), mpCallbackRecorder->m_aSelection[0].GetHeight());
-
+    // Search again - same result
     lcl_search("ABC");
+
     CPPUNIT_ASSERT_EQUAL(true, mpCallbackRecorder->m_bFound);
+    CPPUNIT_ASSERT_EQUAL(2, mpCallbackRecorder->m_nSearchResultCount);
 
     CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultSelection.size());
+    CPPUNIT_ASSERT_EQUAL(OString("3763, 1331, 1432, 483"),
+                         mpCallbackRecorder->m_aSearchResultSelection[0]);
+    CPPUNIT_ASSERT_EQUAL(tools::Rectangle(Point(3763, 1331), Size(1433, 484)),
+                         mpCallbackRecorder->m_aSelection[0]);
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(LOKitSearchTest);
commit 97cdbd4f3719f10942865db01efb7e9b62db6bfa
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Fri May 29 23:52:50 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 15:07:11 2020 +0200

    vcl: add search start position support for VectorGraphicSearch
    
    By default we start at the begin of the page, but with this change
    make it possible to start at the end. This makes it possible to
    search in the backwards direction (set the start position at to
    the end and search with "previous").
    
    Change-Id: I78fb1461b86bf9eab2f91c3b9a81cbb5c6557332
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95382
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 83d27791fed75941c75d3cc571c3d5cf27d14e8c)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95930
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit d7e75f660b3cdfa43ffa572f7a150263b7ed27e8)

diff --git a/include/vcl/VectorGraphicSearch.hxx b/include/vcl/VectorGraphicSearch.hxx
index a00c212ad61c..b67c63a844d8 100644
--- a/include/vcl/VectorGraphicSearch.hxx
+++ b/include/vcl/VectorGraphicSearch.hxx
@@ -21,6 +21,12 @@
 
 class SearchContext;
 
+enum class SearchStartPosition
+{
+    Begin,
+    End
+};
+
 class VCL_DLLPUBLIC VectorGraphicSearch final
 {
 private:
@@ -29,12 +35,14 @@ private:
     Graphic maGraphic;
     std::unique_ptr<SearchContext> mpSearchContext;
 
-    bool searchPDF(std::shared_ptr<VectorGraphicData> const& rData, OUString const& rSearchString);
+    bool searchPDF(std::shared_ptr<VectorGraphicData> const& rData, OUString const& rSearchString,
+                   SearchStartPosition eStartPosition);
 
 public:
     VectorGraphicSearch(Graphic const& rGraphic);
     ~VectorGraphicSearch();
-    bool search(OUString const& rSearchString);
+    bool search(OUString const& rSearchString,
+                SearchStartPosition eStartPosition = SearchStartPosition::Begin);
     basegfx::B2DSize pageSize();
     bool next();
     bool previous();
diff --git a/vcl/qa/cppunit/VectorGraphicSearchTest.cxx b/vcl/qa/cppunit/VectorGraphicSearchTest.cxx
index 7962c23f4e8f..5f65b4ba7e3d 100644
--- a/vcl/qa/cppunit/VectorGraphicSearchTest.cxx
+++ b/vcl/qa/cppunit/VectorGraphicSearchTest.cxx
@@ -93,32 +93,71 @@ void VectorGraphicSearchTest::testNextPrevious()
     Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
     aGraphic.makeAvailable();
 
-    VectorGraphicSearch aSearch(aGraphic);
-    CPPUNIT_ASSERT_EQUAL(true, aSearch.search("lazy"));
+    { // Start from the beginning of the page
+        VectorGraphicSearch aSearch(aGraphic);
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.search("lazy"));
 
-    // next - first match found
-    CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
-    CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+        // no previous - we are at the begin
+        CPPUNIT_ASSERT_EQUAL(false, aSearch.previous());
+        CPPUNIT_ASSERT_EQUAL(0, aSearch.index()); // nothing was yet found, so it is 0
 
-    // next - second match found
-    CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
-    CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+        // next - first position found
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
+        CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
 
-    // next - not found, index unchanged
-    CPPUNIT_ASSERT_EQUAL(false, aSearch.next());
-    CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+        // next - second position found
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
+        CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
 
-    // previous - first match
-    CPPUNIT_ASSERT_EQUAL(true, aSearch.previous());
-    CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+        // next - not found, index unchanged
+        CPPUNIT_ASSERT_EQUAL(false, aSearch.next());
+        CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
 
-    // previous - not found, index unchanged
-    CPPUNIT_ASSERT_EQUAL(false, aSearch.previous());
-    CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+        // previous - first position
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.previous());
+        CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
 
-    // next - second match found
-    CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
-    CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+        // previous - not found, index unchanged
+        CPPUNIT_ASSERT_EQUAL(false, aSearch.previous());
+        CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+
+        // next - second position found
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
+        CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+    }
+
+    { // Start from the end of the page
+        VectorGraphicSearch aSearch(aGraphic);
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.search("lazy", SearchStartPosition::End));
+
+        // no next - we are at the end
+        CPPUNIT_ASSERT_EQUAL(false, aSearch.next());
+        CPPUNIT_ASSERT_EQUAL(0, aSearch.index()); // nothing was yet found, so it is 0
+
+        // previous - second position found
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.previous());
+        CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+
+        // previous - first position found
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.previous());
+        CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+
+        // previous - not found, index unchanged
+        CPPUNIT_ASSERT_EQUAL(false, aSearch.previous());
+        CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+
+        // next - second position
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
+        CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+
+        // next - not found, index unchanged
+        CPPUNIT_ASSERT_EQUAL(false, aSearch.next());
+        CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+
+        // previous - first match found
+        CPPUNIT_ASSERT_EQUAL(true, aSearch.previous());
+        CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+    }
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(VectorGraphicSearchTest);
diff --git a/vcl/source/graphic/VectorGraphicSearch.cxx b/vcl/source/graphic/VectorGraphicSearch.cxx
index 8b73cab71340..42d98a981ef9 100644
--- a/vcl/source/graphic/VectorGraphicSearch.cxx
+++ b/vcl/source/graphic/VectorGraphicSearch.cxx
@@ -42,14 +42,17 @@ public:
     FPDF_PAGE mpPage;
     FPDF_TEXTPAGE mpTextPage;
     OUString maSearchString;
+    SearchStartPosition meStartPosition;
     FPDF_SCHHANDLE mpSearchHandle;
 
-    SearchContext(FPDF_DOCUMENT pPdfDocument, sal_Int32 nPageIndex, OUString const& rSearchString)
+    SearchContext(FPDF_DOCUMENT pPdfDocument, sal_Int32 nPageIndex, OUString const& rSearchString,
+                  SearchStartPosition eStartPosition)
         : mpPdfDocument(pPdfDocument)
         , mnPageIndex(nPageIndex)
         , mpPage(nullptr)
         , mpTextPage(nullptr)
         , maSearchString(rSearchString)
+        , meStartPosition(eStartPosition)
         , mpSearchHandle(nullptr)
     {
     }
@@ -92,7 +95,17 @@ public:
             return false;
 
         FPDF_WIDESTRING pString = reinterpret_cast<FPDF_WIDESTRING>(maSearchString.getStr());
-        mpSearchHandle = FPDFText_FindStart(mpTextPage, pString, 0, 0);
+
+        // Index where to start to search. -1 => at the end
+        int nStartIndex = meStartPosition == SearchStartPosition::End ? -1 : 0;
+
+        // FPDF_MATCHCASE, FPDF_MATCHWHOLEWORD, FPDF_CONSECUTIVE
+        // FPDF_MATCHCASE - If not set, it will not match case by default.
+        // FPDF_MATCHWHOLEWORD - If not set, it will not match the whole word by default.
+        // FPDF_CONSECUTIVE - If not set, it will skip past the current match to look for the next match.
+        int nSearchFlags = 0;
+
+        mpSearchHandle = FPDFText_FindStart(mpTextPage, pString, nSearchFlags, nStartIndex);
 
         return mpSearchHandle != nullptr;
     }
@@ -183,19 +196,20 @@ VectorGraphicSearch::~VectorGraphicSearch()
     FPDF_DestroyLibrary();
 }
 
-bool VectorGraphicSearch::search(OUString const& rSearchString)
+bool VectorGraphicSearch::search(OUString const& rSearchString, SearchStartPosition eStartPosition)
 {
     auto pData = maGraphic.getVectorGraphicData();
 
     if (pData && pData->getVectorGraphicDataType() == VectorGraphicDataType::Pdf)
     {
-        return searchPDF(pData, rSearchString);
+        return searchPDF(pData, rSearchString, eStartPosition);
     }
     return false;
 }
 
 bool VectorGraphicSearch::searchPDF(std::shared_ptr<VectorGraphicData> const& rData,
-                                    OUString const& rSearchString)
+                                    OUString const& rSearchString,
+                                    SearchStartPosition eStartPosition)
 {
     if (rSearchString.isEmpty())
         return false;
@@ -231,8 +245,8 @@ bool VectorGraphicSearch::searchPDF(std::shared_ptr<VectorGraphicData> const& rD
 
     sal_Int32 nPageIndex = std::max(rData->getPageIndex(), sal_Int32(0));
 
-    mpSearchContext.reset(
-        new SearchContext(mpImplementation->mpPdfDocument, nPageIndex, rSearchString));
+    mpSearchContext.reset(new SearchContext(mpImplementation->mpPdfDocument, nPageIndex,
+                                            rSearchString, eStartPosition));
 
     return mpSearchContext->initialize();
 }
commit e5133f196d97212440219d3b03e5f7c8ffe9f9c6
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Fri May 29 23:26:51 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 15:06:59 2020 +0200

    vcl: add "previous" search to VectorGraphicSearch
    
    Previous moves backwards in the search matches.
    
    Change-Id: I88d402e0b8cb9dc4fd93e7f1ce5b08fb42aadd06
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95381
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit e20440effc7a47c8a5e8ef0943e6872cd9b3646a)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95929
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 9d653f4962aae4dae6c0d4166f020354a5f82711)

diff --git a/include/vcl/VectorGraphicSearch.hxx b/include/vcl/VectorGraphicSearch.hxx
index 5420e161448b..a00c212ad61c 100644
--- a/include/vcl/VectorGraphicSearch.hxx
+++ b/include/vcl/VectorGraphicSearch.hxx
@@ -37,6 +37,7 @@ public:
     bool search(OUString const& rSearchString);
     basegfx::B2DSize pageSize();
     bool next();
+    bool previous();
     int index();
     std::vector<basegfx::B2DRectangle> getTextRectangles();
 };
diff --git a/vcl/qa/cppunit/VectorGraphicSearchTest.cxx b/vcl/qa/cppunit/VectorGraphicSearchTest.cxx
index 01022a3fe225..7962c23f4e8f 100644
--- a/vcl/qa/cppunit/VectorGraphicSearchTest.cxx
+++ b/vcl/qa/cppunit/VectorGraphicSearchTest.cxx
@@ -26,9 +26,11 @@ class VectorGraphicSearchTest : public test::BootstrapFixtureBase
     }
 
     void test();
+    void testNextPrevious();
 
     CPPUNIT_TEST_SUITE(VectorGraphicSearchTest);
     CPPUNIT_TEST(test);
+    CPPUNIT_TEST(testNextPrevious);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -81,6 +83,44 @@ void VectorGraphicSearchTest::test()
     CPPUNIT_ASSERT_DOUBLES_EQUAL(6381.04, aRectangles[3].getMaxY(), 1E-2);
 }
 
+// Test next and previous work as expected to move
+// between search matches.
+void VectorGraphicSearchTest::testNextPrevious()
+{
+    OUString aURL = getFullUrl("Pangram.pdf");
+    SvFileStream aStream(aURL, StreamMode::READ);
+    GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+    Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(aStream);
+    aGraphic.makeAvailable();
+
+    VectorGraphicSearch aSearch(aGraphic);
+    CPPUNIT_ASSERT_EQUAL(true, aSearch.search("lazy"));
+
+    // next - first match found
+    CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
+    CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+
+    // next - second match found
+    CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
+    CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+
+    // next - not found, index unchanged
+    CPPUNIT_ASSERT_EQUAL(false, aSearch.next());
+    CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+
+    // previous - first match
+    CPPUNIT_ASSERT_EQUAL(true, aSearch.previous());
+    CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+
+    // previous - not found, index unchanged
+    CPPUNIT_ASSERT_EQUAL(false, aSearch.previous());
+    CPPUNIT_ASSERT_EQUAL(34, aSearch.index());
+
+    // next - second match found
+    CPPUNIT_ASSERT_EQUAL(true, aSearch.next());
+    CPPUNIT_ASSERT_EQUAL(817, aSearch.index());
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(VectorGraphicSearchTest);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/graphic/VectorGraphicSearch.cxx b/vcl/source/graphic/VectorGraphicSearch.cxx
index d54f32930276..8b73cab71340 100644
--- a/vcl/source/graphic/VectorGraphicSearch.cxx
+++ b/vcl/source/graphic/VectorGraphicSearch.cxx
@@ -104,6 +104,13 @@ public:
         return false;
     }
 
+    bool previous()
+    {
+        if (mpSearchHandle)
+            return FPDFText_FindPrev(mpSearchHandle);
+        return false;
+    }
+
     int index()
     {
         if (mpSearchHandle)
@@ -245,6 +252,13 @@ bool VectorGraphicSearch::next()
     return false;
 }
 
+bool VectorGraphicSearch::previous()
+{
+    if (mpSearchContext)
+        return mpSearchContext->previous();
+    return false;
+}
+
 int VectorGraphicSearch::index()
 {
     if (mpSearchContext)
commit dfa1a57c62edb48c1b3ca5f454074f66e899cfd6
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Tue Jun 2 23:15:03 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 15:06:41 2020 +0200

    sd: use getPDFSelection to get the search selection for LOKit
    
    This reduces code duplication.
    
    Change-Id: I0a6a44d696841d1573d23f87353ac055cc92c83a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95386
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit fcc13ba6a0121cfdf70d1f39318a024777247559)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95928
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit e1c7df9482175dff16baa8720d4f0e962b671a35)

diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index 7b169c5f1a72..6073ab1ec0d4 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -759,26 +759,12 @@ void SdOutliner::sendLOKSearchResultCallback(std::shared_ptr<sd::ViewShell> & pV
     std::vector<::tools::Rectangle> aLogicRects;
     if (mpImpl->mbCurrentIsVectorGraphic)
     {
-        basegfx::B2DSize aPdfPageSize = mpImpl->mpVectorGraphicSearch->pageSize();
+        basegfx::B2DRectangle aSelectionHMM = getPDFSelection(mpImpl->mpVectorGraphicSearch, mpObj);
 
-        tools::Rectangle aObjectRectTwip = OutputDevice::LogicToLogic(mpObj->GetLogicRect(), MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
-        basegfx::B2DRectangle aObjectB2DRectTwip(vcl::unotools::b2DRectangleFromRectangle(aObjectRectTwip));
-
-        // Setup coordinate conversion matrix to convert the inner PDF
-        // coordinates to the page relative coordinates
-        basegfx::B2DHomMatrix aB2DMatrix;
-
-        aB2DMatrix.scale(aObjectB2DRectTwip.getWidth() / aPdfPageSize.getX(),
-                         aObjectB2DRectTwip.getHeight() / aPdfPageSize.getY());
-
-        aB2DMatrix.translate(aObjectB2DRectTwip.getMinX(), aObjectB2DRectTwip.getMinY());
-
-        for (auto const & rRectangle : mpImpl->mpVectorGraphicSearch->getTextRectangles())
-        {
-            basegfx::B2DRectangle aRectangle(rRectangle);
-            aRectangle *= aB2DMatrix;
-            aLogicRects.emplace_back(Point(aRectangle.getMinX(), aRectangle.getMinY()), Size(aRectangle.getWidth(), aRectangle.getHeight()));
-        }
+        tools::Rectangle aSelection(Point(aSelectionHMM.getMinX(), aSelectionHMM.getMinY()),
+                                    Size(aSelectionHMM.getWidth(), aSelectionHMM.getHeight()));
+        aSelection = OutputDevice::LogicToLogic(aSelection, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
+        aLogicRects.push_back(aSelection);
     }
     else
     {
commit 9fbe629bdb74324990b002c1a010ca58085040b9
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Fri May 29 23:06:57 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 15:00:49 2020 +0200

    sd: fix not found case in PDF search + add PDF Search tests
    
    When searching the PDF and the search text is not found (anymore)
    in the current VectorGraphicSearch, we need to remove it and mark
    that we don't currently search in a vector graphic (PDF) anymore.
    This wasn't handled correctly and caused a crash.
    
    In addition add a LOKit test for search into a PDF document, to
    make sure the not-found case and usual searching case are working
    correctly.
    
    Change-Id: I663a6b2cf4879f11d62e440ea0c35ffcd205f81f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95380
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit a99aef3cf0a8ff3f04077d1530d1602505cecaae)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95927
    (cherry picked from commit fb81d3f064b24a4fa0585eaac4e5811443a45768)

diff --git a/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
index 4474bd8e4751..33257f12d4ab 100644
--- a/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
+++ b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
@@ -24,10 +24,14 @@
 #include <sfx2/viewfrm.hxx>
 #include <svl/srchitem.hxx>
 #include <svl/stritem.hxx>
+#include <vcl/scheduler.hxx>
 #include <ViewShellBase.hxx>
 #include <ViewShell.hxx>
 #include <unomodel.hxx>
 
+#include <sdpage.hxx>
+#include <svx/svdograf.hxx>
+
 #include <com/sun/star/frame/Desktop.hpp>
 
 using namespace css;
@@ -38,7 +42,7 @@ private:
     static constexpr char DATA_DIRECTORY[] = "/sd/qa/unit/tiledrendering/data/";
 
 public:
-    LOKitSearchTest() {}
+    LOKitSearchTest() = default;
 
     virtual void setUp() override;
     virtual void tearDown() override;
@@ -49,6 +53,8 @@ public:
     void testSearchAllNotifications();
     void testSearchAllFollowedBySearch();
     void testDontSearchInMasterPages();
+    void testSearchInPDFNonExisting();
+    void testSearchInPDF();
 
     CPPUNIT_TEST_SUITE(LOKitSearchTest);
     CPPUNIT_TEST(testSearch);
@@ -57,6 +63,8 @@ public:
     CPPUNIT_TEST(testSearchAllNotifications);
     CPPUNIT_TEST(testSearchAllFollowedBySearch);
     CPPUNIT_TEST(testDontSearchInMasterPages);
+    CPPUNIT_TEST(testSearchInPDFNonExisting);
+    CPPUNIT_TEST(testSearchInPDF);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -96,9 +104,11 @@ LOKitSearchTest::createDoc(const char* pName, const uno::Sequence<beans::Propert
 {
     if (mxComponent.is())
         mxComponent->dispose();
+
     mxComponent = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY)
-                                      + OUString::createFromAscii(pName),
-                                  "com.sun.star.presentation.PresentationDocument");
+                                  + OUString::createFromAscii(pName));
+
+    CPPUNIT_ASSERT(mxComponent.is());
     SdXImpressDocument* pImpressDocument = dynamic_cast<SdXImpressDocument*>(mxComponent.get());
     CPPUNIT_ASSERT(pImpressDocument);
     pImpressDocument->initializeForTiledRendering(rArguments);
@@ -109,15 +119,20 @@ namespace
 {
 void lcl_search(const OUString& rKey, bool bFindAll = false)
 {
+    Scheduler::ProcessEventsToIdle();
+    SvxSearchCmd eSearch = bFindAll ? SvxSearchCmd::FIND_ALL : SvxSearchCmd::FIND;
+
     uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
         { "SearchItem.SearchString", uno::makeAny(rKey) },
         { "SearchItem.Backward", uno::makeAny(false) },
-        { "SearchItem.Command", uno::makeAny(static_cast<sal_uInt16>(
-                                    bFindAll ? SvxSearchCmd::FIND_ALL : SvxSearchCmd::FIND)) },
+        { "SearchItem.Command", uno::makeAny(sal_uInt16(eSearch)) },
     }));
+
     comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+    Scheduler::ProcessEventsToIdle();
 }
-}
+
+} // end anonymous namespace
 
 void LOKitSearchTest::testSearch()
 {
@@ -228,6 +243,72 @@ void LOKitSearchTest::testDontSearchInMasterPages()
     CPPUNIT_ASSERT_EQUAL(false, mpCallbackRecorder->m_bFound);
 }
 
+void LOKitSearchTest::testSearchInPDFNonExisting()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("PDFSearch.pdf");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    CPPUNIT_ASSERT(pViewShell);
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+
+    SdPage* pPage = pViewShell->GetActualPage();
+    CPPUNIT_ASSERT(pPage);
+
+    SdrObject* pObject = pPage->GetObj(0);
+    CPPUNIT_ASSERT(pObject);
+
+    SdrGrafObj* pGraphicObject = dynamic_cast<SdrGrafObj*>(pObject);
+    CPPUNIT_ASSERT(pGraphicObject);
+
+    Graphic aGraphic = pGraphicObject->GetGraphic();
+    auto const& pVectorGraphicData = aGraphic.getVectorGraphicData();
+    CPPUNIT_ASSERT(pVectorGraphicData);
+    CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf,
+                         pVectorGraphicData->getVectorGraphicDataType());
+
+    lcl_search("NonExisting");
+
+    CPPUNIT_ASSERT_EQUAL(false, mpCallbackRecorder->m_bFound);
+}
+
+void LOKitSearchTest::testSearchInPDF()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("PDFSearch.pdf");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    CPPUNIT_ASSERT(pViewShell);
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+
+    SdPage* pPage = pViewShell->GetActualPage();
+    CPPUNIT_ASSERT(pPage);
+
+    SdrObject* pObject = pPage->GetObj(0);
+    CPPUNIT_ASSERT(pObject);
+
+    SdrGrafObj* pGraphicObject = dynamic_cast<SdrGrafObj*>(pObject);
+    CPPUNIT_ASSERT(pGraphicObject);
+
+    Graphic aGraphic = pGraphicObject->GetGraphic();
+    auto const& pVectorGraphicData = aGraphic.getVectorGraphicData();
+    CPPUNIT_ASSERT(pVectorGraphicData);
+    CPPUNIT_ASSERT_EQUAL(VectorGraphicDataType::Pdf,
+                         pVectorGraphicData->getVectorGraphicDataType());
+
+    lcl_search("ABC");
+
+    CPPUNIT_ASSERT_EQUAL(true, mpCallbackRecorder->m_bFound);
+
+    CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultSelection.size());
+
+    CPPUNIT_ASSERT_EQUAL(long(3763), mpCallbackRecorder->m_aSelection[0].Left());
+    CPPUNIT_ASSERT_EQUAL(long(1331), mpCallbackRecorder->m_aSelection[0].Top());
+    CPPUNIT_ASSERT_EQUAL(long(1433), mpCallbackRecorder->m_aSelection[0].GetWidth());
+    CPPUNIT_ASSERT_EQUAL(long(484), mpCallbackRecorder->m_aSelection[0].GetHeight());
+
+    lcl_search("ABC");
+    CPPUNIT_ASSERT_EQUAL(true, mpCallbackRecorder->m_bFound);
+
+    CPPUNIT_ASSERT_EQUAL(size_t(1), mpCallbackRecorder->m_aSearchResultSelection.size());
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(LOKitSearchTest);
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/qa/unit/tiledrendering/data/PDFSearch.pdf b/sd/qa/unit/tiledrendering/data/PDFSearch.pdf
new file mode 100644
index 000000000000..ea8a0919a4a1
Binary files /dev/null and b/sd/qa/unit/tiledrendering/data/PDFSearch.pdf differ
diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index a96e284b1de8..7b169c5f1a72 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -881,6 +881,11 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti
                         aSubSelections.push_back(aSubSelection);
                     mpView->MarkObj(mpObj, pPageView, false, false, aSubSelections);
                 }
+                else
+                {
+                    mpImpl->mbCurrentIsVectorGraphic = false;
+                    mpImpl->mpVectorGraphicSearch.reset();
+                }
             }
             else
             {
@@ -1287,6 +1292,16 @@ void SdOutliner::ProvideNextTextObject()
 
                             mpDrawDocument->GetDocSh()->SetWaitCursor( false );
                         }
+                        else
+                        {
+                            mpImpl->mbCurrentIsVectorGraphic = false;
+                            mpImpl->mpVectorGraphicSearch.reset();
+                        }
+                    }
+                    else
+                    {
+                        mpImpl->mbCurrentIsVectorGraphic = false;
+                        mpImpl->mpVectorGraphicSearch.reset();
                     }
                 }
                 else
commit 3b4ac52b9ba6adeca03aec0c93736283743189f5
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Fri May 29 08:36:34 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 14:53:25 2020 +0200

    sd: use b2DRectangleFromRectangle from canvastools.hxx
    
    Change-Id: I8604b80f887a2c3a1143b8a9bc70f429576512f1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95350
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit beb0fc465163e5edc0b7f978ad46c6ad0cd9e2de)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95925
    (cherry picked from commit 2c402eb238ef375971b1604c5dbfe6c6eedc2000)

diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index 6dff606cbf26..a96e284b1de8 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -27,6 +27,7 @@
 #include <editeng/colritem.hxx>
 #include <editeng/eeitem.hxx>
 #include <editeng/editstat.hxx>
+#include <vcl/canvastools.hxx>
 #include <vcl/outdev.hxx>
 #include <svx/dlgutil.hxx>
 #include <svx/xtable.hxx>
@@ -711,15 +712,6 @@ bool SdOutliner::SearchAndReplaceAll()
 
 namespace
 {
-basegfx::B2DRange b2DRectangleFromRectangle( const ::tools::Rectangle& rRect )
-{
-    if (rRect.IsWidthEmpty() && rRect.IsHeightEmpty())
-        return basegfx::B2DRange(basegfx::B2DTuple(rRect.Left(), rRect.Top()));
-    return basegfx::B2DRectangle(rRect.Left(),
-                                 rRect.Top(),
-                                 rRect.IsWidthEmpty() ? rRect.Left() : rRect.Right(),
-                                 rRect.IsHeightEmpty() ? rRect.Top() : rRect.Bottom());
-}
 
 basegfx::B2DRectangle getPDFSelection(std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch,
                                        SdrObject* pObject)
@@ -732,7 +724,7 @@ basegfx::B2DRectangle getPDFSelection(std::unique_ptr<VectorGraphicSearch> & rVe
 
     basegfx::B2DSize aPdfPageSizeHMM = rVectorGraphicSearch->pageSize();
 
-    basegfx::B2DRectangle aObjectB2DRectHMM(b2DRectangleFromRectangle(pObject->GetLogicRect()));
+    basegfx::B2DRectangle aObjectB2DRectHMM(vcl::unotools::b2DRectangleFromRectangle(pObject->GetLogicRect()));
 
     // Setup coordinate conversion matrix to convert the inner PDF
     // coordinates to the page relative coordinates
@@ -770,7 +762,7 @@ void SdOutliner::sendLOKSearchResultCallback(std::shared_ptr<sd::ViewShell> & pV
         basegfx::B2DSize aPdfPageSize = mpImpl->mpVectorGraphicSearch->pageSize();
 
         tools::Rectangle aObjectRectTwip = OutputDevice::LogicToLogic(mpObj->GetLogicRect(), MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
-        basegfx::B2DRectangle aObjectB2DRectTwip(b2DRectangleFromRectangle(aObjectRectTwip));
+        basegfx::B2DRectangle aObjectB2DRectTwip(vcl::unotools::b2DRectangleFromRectangle(aObjectRectTwip));
 
         // Setup coordinate conversion matrix to convert the inner PDF
         // coordinates to the page relative coordinates
commit 2f3ef47e64384c03e908ae56b7da97a13b78858c
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Fri May 29 08:33:42 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 14:53:07 2020 +0200

    sd: remove code dupl. when getting PDF text selection for LOK
    
    Change-Id: I61d0a9851e9cfe60e9672acec38946b0b25f310f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95349
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 06ddbd4a7a15b80b0286b359bd0a05812fd7ee75)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95924
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 42cfe2309bd9731d0e5b497356af5834757521bb)

diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index 7d8ba3eb9e13..6dff606cbf26 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -721,11 +721,16 @@ basegfx::B2DRange b2DRectangleFromRectangle( const ::tools::Rectangle& rRect )
                                  rRect.IsHeightEmpty() ? rRect.Top() : rRect.Bottom());
 }
 
-void getPDFSelections(std::vector<basegfx::B2DRectangle> & rSubSelections,
-                      std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch,
-                      SdrObject* pObject)
+basegfx::B2DRectangle getPDFSelection(std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch,
+                                       SdrObject* pObject)
 {
-    basegfx::B2DSize aPdfPageSize = rVectorGraphicSearch->pageSize();
+    basegfx::B2DRectangle aSelection;
+
+    auto const & rTextRectangles = rVectorGraphicSearch->getTextRectangles();
+    if (rTextRectangles.empty())
+        return aSelection;
+
+    basegfx::B2DSize aPdfPageSizeHMM = rVectorGraphicSearch->pageSize();
 
     basegfx::B2DRectangle aObjectB2DRectHMM(b2DRectangleFromRectangle(pObject->GetLogicRect()));
 
@@ -733,24 +738,24 @@ void getPDFSelections(std::vector<basegfx::B2DRectangle> & rSubSelections,
     // coordinates to the page relative coordinates
     basegfx::B2DHomMatrix aB2DMatrix;
 
-    aB2DMatrix.scale(aObjectB2DRectHMM.getWidth() / aPdfPageSize.getX(),
-                     aObjectB2DRectHMM.getHeight() / aPdfPageSize.getY());
+    aB2DMatrix.scale(aObjectB2DRectHMM.getWidth() / aPdfPageSizeHMM.getX(),
+                     aObjectB2DRectHMM.getHeight() / aPdfPageSizeHMM.getY());
 
     aB2DMatrix.translate(aObjectB2DRectHMM.getMinX(), aObjectB2DRectHMM.getMinY());
 
-    basegfx::B2DRectangle aCombined;
 
     for (auto const & rRectangle : rVectorGraphicSearch->getTextRectangles())
     {
         basegfx::B2DRectangle aRectangle(rRectangle);
         aRectangle *= aB2DMatrix;
-        if (aCombined.isEmpty())
-            aCombined = aRectangle;
+
+        if (aSelection.isEmpty())
+            aSelection = aRectangle;
         else
-            aCombined.expand(aRectangle);
+            aSelection.expand(aRectangle);
     }
 
-    rSubSelections.push_back(aCombined);
+    return aSelection;
 }
 
 } // end namespace
@@ -879,7 +884,9 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti
                     mpView->UnmarkAllObj(pPageView);
 
                     std::vector<basegfx::B2DRectangle> aSubSelections;
-                    getPDFSelections(aSubSelections, mpImpl->mpVectorGraphicSearch, mpObj);
+                    basegfx::B2DRectangle aSubSelection = getPDFSelection(mpImpl->mpVectorGraphicSearch, mpObj);
+                    if (!aSubSelection.isEmpty())
+                        aSubSelections.push_back(aSubSelection);
                     mpView->MarkObj(mpObj, pPageView, false, false, aSubSelections);
                 }
             }
@@ -1280,7 +1287,10 @@ void SdOutliner::ProvideNextTextObject()
                             mpView->UnmarkAllObj(pPageView);
 
                             std::vector<basegfx::B2DRectangle> aSubSelections;
-                            getPDFSelections(aSubSelections, mpImpl->mpVectorGraphicSearch, mpObj);
+                            basegfx::B2DRectangle aSubSelection = getPDFSelection(mpImpl->mpVectorGraphicSearch, mpObj);
+                            if (!aSubSelection.isEmpty())
+                                aSubSelections.push_back(aSubSelection);
+
                             mpView->MarkObj(mpObj, pPageView, false, false, aSubSelections);
 
                             mpDrawDocument->GetDocSh()->SetWaitCursor( false );
commit e17d264a2bf73bf3a0ce125e9d373d2a08023f0e
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Thu May 28 08:07:51 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 14:52:45 2020 +0200

    sd: Search inside PDF document that were inserted as a graphic
    
    This implements searching inside PDF documents that were inserted
    into the Draw/Impress document as a graphics and marks the areas
    of text that matches the search string. Complex (regex) search is
    not supported.
    
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95348
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 7a84dffb44d4b1fa6e2a3cd3e3dc7d942f3c3e80)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95923
    (cherry picked from commit 894b1a2f412ecc889a9932df3935c69e30852c6f)
    
    Change-Id: I55d67772a2fe876ae72b9164998347304025d3e0

diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index ef8204ad5b80..7d8ba3eb9e13 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -21,6 +21,7 @@
 #include <boost/property_tree/json_parser.hpp>
 #include <vcl/wrkwin.hxx>
 #include <vcl/settings.hxx>
+#include <vcl/VectorGraphicSearch.hxx>
 
 #include <svl/srchitem.hxx>
 #include <editeng/colritem.hxx>
@@ -35,6 +36,7 @@
 #include <svx/svxerr.hxx>
 #include <svx/svdotext.hxx>
 #include <svx/svdotable.hxx>
+#include <svx/svdograf.hxx>
 #include <editeng/unolingu.hxx>
 #include <svx/svditer.hxx>
 #include <com/sun/star/linguistic2/XSpellChecker1.hpp>
@@ -111,6 +113,11 @@ public:
     */
     void ReleaseOutlinerView();
 
+    /** Search in vector graphic
+     */
+    bool mbCurrentIsVectorGraphic;
+    std::unique_ptr<VectorGraphicSearch> mpVectorGraphicSearch;
+
 private:
     /** Flag that specifies whether we own the outline view pointed to by
         <member>mpOutlineView</member> and thus have to
@@ -702,21 +709,93 @@ bool SdOutliner::SearchAndReplaceAll()
     return bRet;
 }
 
+namespace
+{
+basegfx::B2DRange b2DRectangleFromRectangle( const ::tools::Rectangle& rRect )
+{
+    if (rRect.IsWidthEmpty() && rRect.IsHeightEmpty())
+        return basegfx::B2DRange(basegfx::B2DTuple(rRect.Left(), rRect.Top()));
+    return basegfx::B2DRectangle(rRect.Left(),
+                                 rRect.Top(),
+                                 rRect.IsWidthEmpty() ? rRect.Left() : rRect.Right(),
+                                 rRect.IsHeightEmpty() ? rRect.Top() : rRect.Bottom());
+}
+
+void getPDFSelections(std::vector<basegfx::B2DRectangle> & rSubSelections,
+                      std::unique_ptr<VectorGraphicSearch> & rVectorGraphicSearch,
+                      SdrObject* pObject)
+{
+    basegfx::B2DSize aPdfPageSize = rVectorGraphicSearch->pageSize();
+
+    basegfx::B2DRectangle aObjectB2DRectHMM(b2DRectangleFromRectangle(pObject->GetLogicRect()));
+
+    // Setup coordinate conversion matrix to convert the inner PDF
+    // coordinates to the page relative coordinates
+    basegfx::B2DHomMatrix aB2DMatrix;
+
+    aB2DMatrix.scale(aObjectB2DRectHMM.getWidth() / aPdfPageSize.getX(),
+                     aObjectB2DRectHMM.getHeight() / aPdfPageSize.getY());
+
+    aB2DMatrix.translate(aObjectB2DRectHMM.getMinX(), aObjectB2DRectHMM.getMinY());
+
+    basegfx::B2DRectangle aCombined;
+
+    for (auto const & rRectangle : rVectorGraphicSearch->getTextRectangles())
+    {
+        basegfx::B2DRectangle aRectangle(rRectangle);
+        aRectangle *= aB2DMatrix;
+        if (aCombined.isEmpty())
+            aCombined = aRectangle;
+        else
+            aCombined.expand(aRectangle);
+    }
+
+    rSubSelections.push_back(aCombined);
+}
+
+} // end namespace
+
 void SdOutliner::sendLOKSearchResultCallback(std::shared_ptr<sd::ViewShell> & pViewShell,
                                              OutlinerView* pOutlinerView,
                                              std::vector<sd::SearchSelection>* pSelections)
 {
     std::vector<::tools::Rectangle> aLogicRects;
-    pOutlinerView->GetSelectionRectangles(aLogicRects);
+    if (mpImpl->mbCurrentIsVectorGraphic)
+    {
+        basegfx::B2DSize aPdfPageSize = mpImpl->mpVectorGraphicSearch->pageSize();
+
+        tools::Rectangle aObjectRectTwip = OutputDevice::LogicToLogic(mpObj->GetLogicRect(), MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
+        basegfx::B2DRectangle aObjectB2DRectTwip(b2DRectangleFromRectangle(aObjectRectTwip));
+
+        // Setup coordinate conversion matrix to convert the inner PDF
+        // coordinates to the page relative coordinates
+        basegfx::B2DHomMatrix aB2DMatrix;
+
+        aB2DMatrix.scale(aObjectB2DRectTwip.getWidth() / aPdfPageSize.getX(),
+                         aObjectB2DRectTwip.getHeight() / aPdfPageSize.getY());
+
+        aB2DMatrix.translate(aObjectB2DRectTwip.getMinX(), aObjectB2DRectTwip.getMinY());
 
-    // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this
-    // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles
-    // which makes that method unusable for others
-    if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit())
+        for (auto const & rRectangle : mpImpl->mpVectorGraphicSearch->getTextRectangles())
+        {
+            basegfx::B2DRectangle aRectangle(rRectangle);
+            aRectangle *= aB2DMatrix;
+            aLogicRects.emplace_back(Point(aRectangle.getMinX(), aRectangle.getMinY()), Size(aRectangle.getWidth(), aRectangle.getHeight()));
+        }
+    }
+    else
     {
-        for (tools::Rectangle& rRectangle : aLogicRects)
+        pOutlinerView->GetSelectionRectangles(aLogicRects);
+
+        // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this
+        // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles
+        // which makes that method unusable for others
+        if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit())
         {
-            rRectangle = OutputDevice::LogicToLogic(rRectangle, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
+            for (tools::Rectangle& rRectangle : aLogicRects)
+            {
+                rRectangle = OutputDevice::LogicToLogic(rRectangle, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
+            }
         }
     }
 
@@ -752,6 +831,11 @@ void SdOutliner::sendLOKSearchResultCallback(std::shared_ptr<sd::ViewShell> & pV
         boost::property_tree::write_json(aStream, aTree);
         aPayload = aStream.str().c_str();
         rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr());
+
+        if (mpImpl->mbCurrentIsVectorGraphic)
+        {
+            rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles.getStr());
+        }
     }
     else
     {
@@ -783,19 +867,40 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti
 
         if (nullptr != dynamic_cast<const sd::DrawViewShell*>(pViewShell.get()))
         {
-            // When replacing we first check if there is a selection
-            // indicating a match.  If there is then replace it.  The
-            // following call to StartSearchAndReplace will then search for
-            // the next match.
-            if (meMode == SEARCH
-                && mpSearchItem->GetCommand() == SvxSearchCmd::REPLACE)
-                if (pOutlinerView->GetSelection().HasRange())
-                    pOutlinerView->StartSearchAndReplace(*mpSearchItem);
-
-            // Search for the next match.
             sal_uLong nMatchCount = 0;
-            if (mpSearchItem->GetCommand() != SvxSearchCmd::REPLACE_ALL)
-                nMatchCount = pOutlinerView->StartSearchAndReplace(*mpSearchItem);
+
+            if (mpImpl->mbCurrentIsVectorGraphic)
+            {
+                if (mpImpl->mpVectorGraphicSearch->next())
+                {
+                    nMatchCount = 1;
+
+                    SdrPageView* pPageView = mpView->GetSdrPageView();
+                    mpView->UnmarkAllObj(pPageView);
+
+                    std::vector<basegfx::B2DRectangle> aSubSelections;
+                    getPDFSelections(aSubSelections, mpImpl->mpVectorGraphicSearch, mpObj);
+                    mpView->MarkObj(mpObj, pPageView, false, false, aSubSelections);
+                }
+            }
+            else
+            {
+                // When replacing we first check if there is a selection
+                // indicating a match.  If there is then replace it.  The
+                // following call to StartSearchAndReplace will then search for
+                // the next match.
+                if (meMode == SEARCH && mpSearchItem->GetCommand() == SvxSearchCmd::REPLACE)
+                {
+                    if (pOutlinerView->GetSelection().HasRange())
+                        pOutlinerView->StartSearchAndReplace(*mpSearchItem);
+                }
+
+                // Search for the next match.
+                if (mpSearchItem->GetCommand() != SvxSearchCmd::REPLACE_ALL)
+                {
+                    nMatchCount = pOutlinerView->StartSearchAndReplace(*mpSearchItem);
+                }
+            }
 
             // Go to the next text object when there have been no matches in
             // the current object or the whole object has already been
@@ -1062,6 +1167,20 @@ bool lclIsValidTextObject(const sd::outliner::IteratorPosition& rPosition)
     return (pObject != nullptr) && pObject->HasText() && ! pObject->IsEmptyPresObj();
 }
 
+bool isValidVectorGraphicObject(const sd::outliner::IteratorPosition& rPosition)
+{
+    auto* pGraphicObject = dynamic_cast<SdrGrafObj*>(rPosition.mxObject.get());
+    if (pGraphicObject)
+    {
+        auto const& pVectorGraphicData = pGraphicObject->GetGraphic().getVectorGraphicData();
+        if (pVectorGraphicData && VectorGraphicDataType::Pdf == pVectorGraphicData->getVectorGraphicDataType())
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 } // end anonymous namespace
 
 
@@ -1077,6 +1196,9 @@ void SdOutliner::ProvideNextTextObject()
     mbEndOfSearch = false;
     mbFoundObject = false;
 
+    // reset the vector search
+    mpImpl->mpVectorGraphicSearch.reset();
+
     mpView->UnmarkAllObj (mpView->GetSdrPageView());
     try
     {
@@ -1109,35 +1231,83 @@ void SdOutliner::ProvideNextTextObject()
             // LOK: do not descent to notes or master pages when searching
             bool bForbiddenPage = comphelper::LibreOfficeKit::isActive() && (maCurrentPosition.mePageKind != PageKind::Standard || maCurrentPosition.meEditMode != EditMode::Page);
 
-            // Switch to the current object only if it is a valid text object.
-            if (!bForbiddenPage && lclIsValidTextObject(maCurrentPosition))
+            mpImpl->mbCurrentIsVectorGraphic = false;
+
+            if (!bForbiddenPage)
             {
-                // Don't set yet in case of searching: the text object may not match.
-                if (meMode != SEARCH)
-                    mpObj = SetObject(maCurrentPosition);
-                else
+                // Switch to the current object only if it is a valid text object.
+                if (lclIsValidTextObject(maCurrentPosition))
+                {
+                    // Don't set yet in case of searching: the text object may not match.
+                    if (meMode != SEARCH)
+                        mpObj = SetObject(maCurrentPosition);
+                    else
+                        mpObj = maCurrentPosition.mxObject.get();
+                }
+                // Or if the object is a valid graphic object which contains vector graphic
+                else if (meMode == SEARCH && isValidVectorGraphicObject(maCurrentPosition))
+                {
                     mpObj = maCurrentPosition.mxObject.get();
+                    mpImpl->mbCurrentIsVectorGraphic = true;
+                }
             }
+
+            // Advance to the next object
             ++maObjectIterator;
 
             if (mpObj)
             {
-                PutTextIntoOutliner();
+                if (mpImpl->mbCurrentIsVectorGraphic)
+                {
+                    // We know here the object is a SdrGrafObj and that it
+                    // contains a vector graphic
+                    auto* pGraphicObject = static_cast<SdrGrafObj*>(mpObj);
+                    OUString const & rString = mpSearchItem->GetSearchString();
+
+                    mpImpl->mpVectorGraphicSearch = std::make_unique<VectorGraphicSearch>(pGraphicObject->GetGraphic());
+                    if (mpImpl->mpVectorGraphicSearch->search(rString))
+                    {
+                        bool bResult = mpImpl->mpVectorGraphicSearch->next();
+                        if (bResult)
+                        {
+                            mpObj = SetObject(maCurrentPosition);
 
-                std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
-                if (pViewShell != nullptr)
-                    switch (meMode)
+                            mbStringFound = true;
+                            mbMatchMayExist = true;
+                            mbFoundObject = true;
+
+                            SdrPageView* pPageView = mpView->GetSdrPageView();
+                            mpView->UnmarkAllObj(pPageView);
+
+                            std::vector<basegfx::B2DRectangle> aSubSelections;
+                            getPDFSelections(aSubSelections, mpImpl->mpVectorGraphicSearch, mpObj);
+                            mpView->MarkObj(mpObj, pPageView, false, false, aSubSelections);
+
+                            mpDrawDocument->GetDocSh()->SetWaitCursor( false );
+                        }
+                    }
+                }
+                else
+                {
+                    PutTextIntoOutliner();
+
+                    std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
+                    if (pViewShell != nullptr)
                     {
-                        case SEARCH:
-                            PrepareSearchAndReplace ();
-                            break;
-                        case SPELL:
-                            PrepareSpellCheck ();
-                            break;
-                        case TEXT_CONVERSION:
-                            PrepareConversion();
-                            break;
+                        switch (meMode)
+                        {
+                            case SEARCH:
+                                PrepareSearchAndReplace ();
+                                break;
+                            case SPELL:
+                                PrepareSpellCheck ();
+                                break;
+                            case TEXT_CONVERSION:
+                                PrepareConversion();
+                                break;
+                        }
                     }
+                }
             }
         }
         else
@@ -1751,6 +1921,7 @@ VclPtr<vcl::Window> SdOutliner::GetMessageBoxParent()
 
 SdOutliner::Implementation::Implementation()
     : meOriginalEditMode(EditMode::Page),
+      mbCurrentIsVectorGraphic(false),
       mbOwnOutlineView(false),
       mpOutlineView(nullptr)
 {
commit 7000e87fa207612f590ee72062a92fb0d805bf34
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Fri May 29 18:18:40 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 14:36:08 2020 +0200

    sd: move LOK search tests out of tiledrendering.cxx
    
    Change-Id: I76ff3e251afc877b0dcf54d772da95bb7a60e823
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95351
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit b40f1d657affbe87f60bfd358199a91c9fea7454)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95926
    (cherry picked from commit 38f68c0b81e62def94fd4f90e6fb1804b7ef0f82)

diff --git a/sd/CppunitTest_sd_tiledrendering.mk b/sd/CppunitTest_sd_tiledrendering.mk
index ffdfbfeed551..a978919c4c3c 100644
--- a/sd/CppunitTest_sd_tiledrendering.mk
+++ b/sd/CppunitTest_sd_tiledrendering.mk
@@ -12,6 +12,7 @@
 $(eval $(call gb_CppunitTest_CppunitTest,sd_tiledrendering))
 
 $(eval $(call gb_CppunitTest_add_exception_objects,sd_tiledrendering, \
+    sd/qa/unit/tiledrendering/LOKitSearchTest \
     sd/qa/unit/tiledrendering/tiledrendering \
 ))
 
diff --git a/sd/qa/unit/sdmodeltestbase.hxx b/sd/qa/unit/sdmodeltestbase.hxx
index 153798399cb1..b8231922e4cb 100644
--- a/sd/qa/unit/sdmodeltestbase.hxx
+++ b/sd/qa/unit/sdmodeltestbase.hxx
@@ -71,7 +71,7 @@ pFilterName: <node oor:Name="...">
 pTypeName: <prop oor:Name="Type">...</prop>
 nFormatType: <prop oor:name="Flags">...</prop>
 */
-FileFormat aFileFormats[] =
+static FileFormat aFileFormats[] =
 {
     { "odp",  "impress8", "impress8", "", ODP_FORMAT_TYPE },
     { "ppt",  "MS PowerPoint 97", "impress_MS_PowerPoint_97", "sdfilt", PPT_FORMAT_TYPE },
diff --git a/sd/qa/unit/tiledrendering/CallbackRecorder.hxx b/sd/qa/unit/tiledrendering/CallbackRecorder.hxx
new file mode 100644
index 000000000000..fc5117cce6dc
--- /dev/null
+++ b/sd/qa/unit/tiledrendering/CallbackRecorder.hxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <boost/property_tree/json_parser.hpp>
+#include <comphelper/string.hxx>
+#include <osl/conditn.hxx>
+#include <sfx2/viewsh.hxx>
+
+namespace
+{
+std::vector<OUString> lcl_convertSeparated(const OUString& rString, sal_Unicode nSeparator)
+{
+    std::vector<OUString> aRet;
+
+    sal_Int32 nIndex = 0;
+    do
+    {
+        OUString aToken = rString.getToken(0, nSeparator, nIndex);
+        aToken = aToken.trim();
+        if (!aToken.isEmpty())
+            aRet.push_back(aToken);
+    } while (nIndex >= 0);
+
+    return aRet;
+}
+
+void lcl_convertRectangle(const OUString& rString, tools::Rectangle& rRectangle)
+{
+    uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(rString);
+    CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5);
+    rRectangle.setX(aSeq[0].toInt32());
+    rRectangle.setY(aSeq[1].toInt32());
+    rRectangle.setWidth(aSeq[2].toInt32());
+    rRectangle.setHeight(aSeq[3].toInt32());
+}
+}
+
+struct CallbackRecorder
+{
+    CallbackRecorder()
+        : m_bFound(true)
+        , m_nPart(0)
+        , m_nSelectionBeforeSearchResult(0)
+        , m_nSelectionAfterSearchResult(0)
+    {
+    }
+
+    tools::Rectangle m_aInvalidation;
+    std::vector<::tools::Rectangle> m_aSelection;
+    bool m_bFound;
+    sal_Int32 m_nPart;
+    std::vector<OString> m_aSearchResultSelection;
+    std::vector<int> m_aSearchResultPart;
+    int m_nSelectionBeforeSearchResult;
+    int m_nSelectionAfterSearchResult;
+    /// For document size changed callback.
+    osl::Condition m_aDocumentSizeCondition;
+
+    static void callback(int nType, const char* pPayload, void* pData)
+    {
+        static_cast<CallbackRecorder*>(pData)->processCallback(nType, pPayload);
+    }
+
+    void processCallback(int nType, const char* pPayload)
+    {
+        switch (nType)
+        {
+            case LOK_CALLBACK_INVALIDATE_TILES:
+            {
+                OUString aPayload = OUString::createFromAscii(pPayload);
+                if (aPayload != "EMPTY" && m_aInvalidation.IsEmpty())
+                    lcl_convertRectangle(aPayload, m_aInvalidation);
+            }
+            break;
+            case LOK_CALLBACK_TEXT_SELECTION:
+            {
+                OUString aPayload = OUString::createFromAscii(pPayload);
+                m_aSelection.clear();
+                for (const OUString& rString : lcl_convertSeparated(aPayload, u';'))
+                {
+                    ::tools::Rectangle aRectangle;
+                    lcl_convertRectangle(rString, aRectangle);
+                    m_aSelection.push_back(aRectangle);
+                }
+                if (m_aSearchResultSelection.empty())
+                    ++m_nSelectionBeforeSearchResult;
+                else
+                    ++m_nSelectionAfterSearchResult;
+            }
+            break;
+            case LOK_CALLBACK_SEARCH_NOT_FOUND:
+            {
+                m_bFound = false;
+            }
+            break;
+            case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED:
+            {
+                m_aDocumentSizeCondition.set();
+            }
+            break;
+            case LOK_CALLBACK_SET_PART:
+            {
+                OUString aPayload = OUString::createFromAscii(pPayload);
+                m_nPart = aPayload.toInt32();
+            }
+            break;
+            case LOK_CALLBACK_SEARCH_RESULT_SELECTION:
+            {
+                m_aSearchResultSelection.clear();
+                m_aSearchResultPart.clear();
+                boost::property_tree::ptree aTree;
+                std::stringstream aStream(pPayload);
+                boost::property_tree::read_json(aStream, aTree);
+                for (const boost::property_tree::ptree::value_type& rValue :
+                     aTree.get_child("searchResultSelection"))
+                {
+                    m_aSearchResultSelection.emplace_back(
+                        rValue.second.get<std::string>("rectangles").c_str());
+                    m_aSearchResultPart.push_back(
+                        std::atoi(rValue.second.get<std::string>("part").c_str()));
+                }
+            }
+            break;
+        }
+    }
+
+    void registerCallbacksFor(SfxViewShell& rViewShell)
+    {
+        rViewShell.registerLibreOfficeKitViewCallback(&CallbackRecorder::callback, this);
+    }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
new file mode 100644
index 000000000000..4474bd8e4751
--- /dev/null
+++ b/sd/qa/unit/tiledrendering/LOKitSearchTest.cxx
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "../sdmodeltestbase.hxx"
+#include "CallbackRecorder.hxx"
+
+#include <test/bootstrapfixture.hxx>
+#include <test/helper/transferable.hxx>
+#include <test/xmltesttools.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/srchitem.hxx>
+#include <svl/stritem.hxx>
+#include <ViewShellBase.hxx>
+#include <ViewShell.hxx>
+#include <unomodel.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+
+using namespace css;
+
+class LOKitSearchTest : public SdModelTestBase, public XmlTestTools
+{
+private:
+    static constexpr char DATA_DIRECTORY[] = "/sd/qa/unit/tiledrendering/data/";
+
+public:
+    LOKitSearchTest() {}
+
+    virtual void setUp() override;
+    virtual void tearDown() override;
+
+    void testSearch();
+    void testSearchAll();
+    void testSearchAllSelections();
+    void testSearchAllNotifications();
+    void testSearchAllFollowedBySearch();
+    void testDontSearchInMasterPages();
+
+    CPPUNIT_TEST_SUITE(LOKitSearchTest);
+    CPPUNIT_TEST(testSearch);
+    CPPUNIT_TEST(testSearchAll);
+    CPPUNIT_TEST(testSearchAllSelections);
+    CPPUNIT_TEST(testSearchAllNotifications);
+    CPPUNIT_TEST(testSearchAllFollowedBySearch);
+    CPPUNIT_TEST(testDontSearchInMasterPages);
+    CPPUNIT_TEST_SUITE_END();
+
+private:
+    SdXImpressDocument* createDoc(const char* pName,
+                                  const uno::Sequence<beans::PropertyValue>& rArguments
+                                  = uno::Sequence<beans::PropertyValue>());
+
+    uno::Reference<lang::XComponent> mxComponent;
+    std::unique_ptr<CallbackRecorder> mpCallbackRecorder;
+};
+
+void LOKitSearchTest::setUp()
+{
+    test::BootstrapFixture::setUp();
+
+    // prevent showing warning message box
+    setenv("OOX_NO_SMARTART_WARNING", "1", 1);
+    comphelper::LibreOfficeKit::setActive(true);
+
+    mxDesktop.set(
+        css::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
+    mpCallbackRecorder = std::make_unique<CallbackRecorder>();
+}
+
+void LOKitSearchTest::tearDown()
+{
+    if (mxComponent.is())
+        mxComponent->dispose();
+
+    comphelper::LibreOfficeKit::setActive(false);
+
+    test::BootstrapFixture::tearDown();
+}
+
+SdXImpressDocument*
+LOKitSearchTest::createDoc(const char* pName, const uno::Sequence<beans::PropertyValue>& rArguments)
+{
+    if (mxComponent.is())
+        mxComponent->dispose();
+    mxComponent = loadFromDesktop(m_directories.getURLFromSrc(DATA_DIRECTORY)
+                                      + OUString::createFromAscii(pName),
+                                  "com.sun.star.presentation.PresentationDocument");
+    SdXImpressDocument* pImpressDocument = dynamic_cast<SdXImpressDocument*>(mxComponent.get());
+    CPPUNIT_ASSERT(pImpressDocument);
+    pImpressDocument->initializeForTiledRendering(rArguments);
+    return pImpressDocument;
+}
+
+namespace
+{
+void lcl_search(const OUString& rKey, bool bFindAll = false)
+{
+    uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence({
+        { "SearchItem.SearchString", uno::makeAny(rKey) },
+        { "SearchItem.Backward", uno::makeAny(false) },
+        { "SearchItem.Command", uno::makeAny(static_cast<sal_uInt16>(
+                                    bFindAll ? SvxSearchCmd::FIND_ALL : SvxSearchCmd::FIND)) },
+    }));
+    comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
+}
+}
+
+void LOKitSearchTest::testSearch()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+    uno::Reference<container::XIndexAccess> xDrawPage(
+        pXImpressDocument->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
+    xShape->setString("Aaa bbb.");
+
+    lcl_search("bbb");
+
+    SdrView* pView = pViewShell->GetView();
+    EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
+    // Did we indeed manage to select the second word?
+    CPPUNIT_ASSERT_EQUAL(OUString("bbb"), rEditView.GetSelected());
+
+    // Did the selection callback fire?
+    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), mpCallbackRecorder->m_aSelection.size());
+
+    // Search for something on the second slide, and make sure that the set-part callback fired.
+    lcl_search("bbb");
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), mpCallbackRecorder->m_nPart);
+    CPPUNIT_ASSERT_EQUAL(true, mpCallbackRecorder->m_bFound);
+    // This was 0; should be 1 match for "find".
+    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1),
+                         mpCallbackRecorder->m_aSearchResultSelection.size());
+    // Result is on the second slide.
+    CPPUNIT_ASSERT_EQUAL(1, mpCallbackRecorder->m_aSearchResultPart[0]);
+
+    // This should trigger the not-found callback.
+    lcl_search("ccc");
+    CPPUNIT_ASSERT_EQUAL(false, mpCallbackRecorder->m_bFound);
+}
+
+void LOKitSearchTest::testSearchAll()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+
+    lcl_search("match", /*bFindAll=*/true);
+
+    // This was empty: find-all did not highlight the first match.
+    CPPUNIT_ASSERT_EQUAL(OString("match"),
+                         apitest::helper::transferable::getTextSelection(
+                             pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));
+
+    // We're on the first slide, search for something on the second slide and make sure we get a SET_PART.
+    mpCallbackRecorder->m_nPart = 0;
+    lcl_search("second", /*bFindAll=*/true);
+    // This was 0: no SET_PART was emitted.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), mpCallbackRecorder->m_nPart);
+}
+
+void LOKitSearchTest::testSearchAllSelections()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+
+    lcl_search("third", /*bFindAll=*/true);
+    // Make sure this is found on the 3rd slide.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), mpCallbackRecorder->m_nPart);
+    // This was 1: only the first match was highlighted.
+    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), mpCallbackRecorder->m_aSelection.size());
+}
+
+void LOKitSearchTest::testSearchAllNotifications()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+
+    lcl_search("third", /*bFindAll=*/true);
+    // Make sure that we get no notifications about selection changes during search.
+    CPPUNIT_ASSERT_EQUAL(0, mpCallbackRecorder->m_nSelectionBeforeSearchResult);
+    // But we do get the selection of the first hit.
+    CPPUNIT_ASSERT(mpCallbackRecorder->m_nSelectionAfterSearchResult > 0);
+}
+
+void LOKitSearchTest::testSearchAllFollowedBySearch()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+
+    lcl_search("third", /*bFindAll=*/true);
+    lcl_search("match" /*,bFindAll=false*/);
+
+    // This used to give wrong result: 'search' after 'search all' still
+    // returned 'third'
+    CPPUNIT_ASSERT_EQUAL(OString("match"),
+                         apitest::helper::transferable::getTextSelection(
+                             pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));
+}
+
+void LOKitSearchTest::testDontSearchInMasterPages()
+{
+    SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
+    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
+    mpCallbackRecorder->registerCallbacksFor(pViewShell->GetViewShellBase());
+
+    // This should trigger the not-found callback ("date" is present only on
+    // the master page)
+    lcl_search("date");
+    CPPUNIT_ASSERT_EQUAL(false, mpCallbackRecorder->m_bFound);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(LOKitSearchTest);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx b/sd/qa/unit/tiledrendering/tiledrendering.cxx
index c26411624b56..56bae59f9e0a 100644
--- a/sd/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx
@@ -57,7 +57,10 @@
 
 using namespace css;
 
-static char const DATA_DIRECTORY[] = "/sd/qa/unit/tiledrendering/data/";
+namespace
+{
+    char const DATA_DIRECTORY[] = "/sd/qa/unit/tiledrendering/data/";
+}
 
 static std::ostream& operator<<(std::ostream& os, ViewShellId id)
 {
@@ -82,12 +85,6 @@ public:
     void testSetGraphicSelection();
     void testUndoShells();
     void testResetSelection();
-    void testSearch();
-    void testSearchAll();
-    void testSearchAllSelections();
-    void testSearchAllNotifications();
-    void testSearchAllFollowedBySearch();
-    void testDontSearchInMasterPages();
     void testInsertDeletePage();
     void testInsertTable();
     void testPartHash();
@@ -136,12 +133,6 @@ public:
     CPPUNIT_TEST(testSetGraphicSelection);
     CPPUNIT_TEST(testUndoShells);
     CPPUNIT_TEST(testResetSelection);
-    CPPUNIT_TEST(testSearch);
-    CPPUNIT_TEST(testSearchAll);
-    CPPUNIT_TEST(testSearchAllSelections);
-    CPPUNIT_TEST(testSearchAllNotifications);
-    CPPUNIT_TEST(testSearchAllFollowedBySearch);
-    CPPUNIT_TEST(testDontSearchInMasterPages);
     CPPUNIT_TEST(testInsertDeletePage);
     CPPUNIT_TEST(testInsertTable);
     CPPUNIT_TEST(testPartHash);
@@ -249,7 +240,10 @@ void SdTiledRenderingTest::callback(int nType, const char* pPayload, void* pData
     static_cast<SdTiledRenderingTest*>(pData)->callbackImpl(nType, pPayload);
 }
 
-static std::vector<OUString> lcl_convertSeparated(const OUString& rString, sal_Unicode nSeparator)
+namespace
+{
+
+std::vector<OUString> lcl_convertSeparated(const OUString& rString, sal_Unicode nSeparator)
 {
     std::vector<OUString> aRet;
 
@@ -266,7 +260,7 @@ static std::vector<OUString> lcl_convertSeparated(const OUString& rString, sal_U
     return aRet;
 }
 
-static void lcl_convertRectangle(const OUString& rString, ::tools::Rectangle& rRectangle)
+void lcl_convertRectangle(const OUString& rString, ::tools::Rectangle& rRectangle)
 {
     uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(rString);
     CPPUNIT_ASSERT(aSeq.getLength() == 4 || aSeq.getLength() == 5);
@@ -276,6 +270,8 @@ static void lcl_convertRectangle(const OUString& rString, ::tools::Rectangle& rR
     rRectangle.setHeight(aSeq[3].toInt32());
 }
 
+} // end anonymouse namespace
+
 void SdTiledRenderingTest::callbackImpl(int nType, const char* pPayload)
 {
     switch (nType)
@@ -582,120 +578,6 @@ void SdTiledRenderingTest::testResetSelection()
     CPPUNIT_ASSERT(!pView->GetTextEditObject());
 }
 
-static void lcl_search(const OUString& rKey, bool bFindAll = false)
-{
-    uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
-    {
-        {"SearchItem.SearchString", uno::makeAny(rKey)},
-        {"SearchItem.Backward", uno::makeAny(false)},
-        {"SearchItem.Command", uno::makeAny(static_cast<sal_uInt16>(bFindAll ? SvxSearchCmd::FIND_ALL : SvxSearchCmd::FIND))},
-    }));
-    comphelper::dispatchCommand(".uno:ExecuteSearch", aPropertyValues);
-}
-
-void SdTiledRenderingTest::testSearch()
-{
-    SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
-    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
-    pViewShell->GetViewShellBase().registerLibreOfficeKitViewCallback(&SdTiledRenderingTest::callback, this);
-    uno::Reference<container::XIndexAccess> xDrawPage(pXImpressDocument->getDrawPages()->getByIndex(0), uno::UNO_QUERY);
-    uno::Reference<text::XTextRange> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY);
-    xShape->setString("Aaa bbb.");
-
-    lcl_search("bbb");
-
-    SdrView* pView = pViewShell->GetView();
-    EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
-    // Did we indeed manage to select the second word?
-    CPPUNIT_ASSERT_EQUAL(OUString("bbb"), rEditView.GetSelected());
-
-    // Did the selection callback fire?
-    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), m_aSelection.size());
-
-    // Search for something on the second slide, and make sure that the set-part callback fired.
-    lcl_search("bbb");
-    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), m_nPart);
-    CPPUNIT_ASSERT_EQUAL(true, m_bFound);
-    // This was 0; should be 1 match for "find".
-    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(1), m_aSearchResultSelection.size());
-    // Result is on the second slide.
-    CPPUNIT_ASSERT_EQUAL(1, m_aSearchResultPart[0]);
-
-    // This should trigger the not-found callback.
-    lcl_search("ccc");
-    CPPUNIT_ASSERT_EQUAL(false, m_bFound);
-}
-
-void SdTiledRenderingTest::testSearchAll()
-{
-    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
-    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
-    pViewShell->GetViewShellBase().registerLibreOfficeKitViewCallback(&SdTiledRenderingTest::callback, this);
-
-    lcl_search("match", /*bFindAll=*/true);
-
-    // This was empty: find-all did not highlight the first match.
-    CPPUNIT_ASSERT_EQUAL(OString("match"), apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));
-
-    // We're on the first slide, search for something on the second slide and make sure we get a SET_PART.
-    m_nPart = 0;
-    lcl_search("second", /*bFindAll=*/true);
-    // This was 0: no SET_PART was emitted.
-    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), m_nPart);
-}
-
-void SdTiledRenderingTest::testSearchAllSelections()
-{
-    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
-    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
-    pViewShell->GetViewShellBase().registerLibreOfficeKitViewCallback(&SdTiledRenderingTest::callback, this);
-
-    lcl_search("third", /*bFindAll=*/true);
-    // Make sure this is found on the 3rd slide.
-    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2), m_nPart);
-    // This was 1: only the first match was highlighted.
-    CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), m_aSelection.size());
-}
-
-void SdTiledRenderingTest::testSearchAllNotifications()
-{
-    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
-    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
-    pViewShell->GetViewShellBase().registerLibreOfficeKitViewCallback(&SdTiledRenderingTest::callback, this);
-
-    lcl_search("third", /*bFindAll=*/true);
-    // Make sure that we get no notifications about selection changes during search.
-    CPPUNIT_ASSERT_EQUAL(0, m_nSelectionBeforeSearchResult);
-    // But we do get the selection of the first hit.
-    CPPUNIT_ASSERT(m_nSelectionAfterSearchResult > 0);
-}
-
-void SdTiledRenderingTest::testSearchAllFollowedBySearch()
-{
-    SdXImpressDocument* pXImpressDocument = createDoc("search-all.odp");
-    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
-    pViewShell->GetViewShellBase().registerLibreOfficeKitViewCallback(&SdTiledRenderingTest::callback, this);
-
-    lcl_search("third", /*bFindAll=*/true);
-    lcl_search("match" /*,bFindAll=false*/);
-
-    // This used to give wrong result: 'search' after 'search all' still
-    // returned 'third'
-    CPPUNIT_ASSERT_EQUAL(OString("match"), apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));
-}
-
-void SdTiledRenderingTest::testDontSearchInMasterPages()
-{
-    SdXImpressDocument* pXImpressDocument = createDoc("dummy.odp");
-    sd::ViewShell* pViewShell = pXImpressDocument->GetDocShell()->GetViewShell();
-    pViewShell->GetViewShellBase().registerLibreOfficeKitViewCallback(&SdTiledRenderingTest::callback, this);
-
-    // This should trigger the not-found callback ("date" is present only on
-    // the master page)
-    lcl_search("date");
-    CPPUNIT_ASSERT_EQUAL(false, m_bFound);
-}
-
 namespace
 {
 
commit 7bcf2daf86d793086f8028b1da9cf167047295ff
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Wed May 27 15:27:14 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 13:36:55 2020 +0200

    svx: add ability to show sub-selections in a marked object
    
    Draws selection rectangles inside the object window.
    
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95347
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit cf812f15b3295a17a9b5d7e3a1c51b00dcbf2629)
    
    Change-Id: I994477426489ea4cea89c86f9e51c9978f16b350
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95921
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit fe69802e22bb2c8365b7542f3f0fbf71cb6182e7)

diff --git a/include/svx/svdmrkv.hxx b/include/svx/svdmrkv.hxx
index c66ddf5bca8e..4b94f630cbb7 100644
--- a/include/svx/svdmrkv.hxx
+++ b/include/svx/svdmrkv.hxx
@@ -26,6 +26,8 @@
 #include <svx/svdtypes.hxx>
 #include <svx/svxdllapi.h>
 #include <o3tl/typed_flags_set.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+
 
 class SfxViewShell;
 
@@ -88,6 +90,7 @@ enum class ImpGetDescriptionOptions
 };
 
 class ImplMarkingOverlay;
+class MarkingSubSelectionOverlay;
 
 class SVX_DLLPUBLIC SdrMarkView : public SdrSnapView
 {
@@ -98,6 +101,8 @@ class SVX_DLLPUBLIC SdrMarkView : public SdrSnapView
     std::unique_ptr<ImplMarkingOverlay> mpMarkPointsOverlay;
     std::unique_ptr<ImplMarkingOverlay> mpMarkGluePointsOverlay;
 
+    std::unique_ptr<MarkingSubSelectionOverlay> mpMarkingSubSelectionOverlay;
+
 protected:
     SdrObject*                  mpMarkedObj;       // If not just one object ( i.e. More than one object ) is marked.
     SdrPageView*                mpMarkedPV;        // If all marked obects are situated on the same PageView.
@@ -106,8 +111,10 @@ protected:
     Point                       maRef2;            // Persistent
     Point                       maLastCrookCenter; // Persistent
     SdrHdlList                  maHdlList;
+
     sdr::ViewSelection          maSdrViewSelection;
 
+    std::vector<basegfx::B2DRectangle> maSubSelectionList;
     tools::Rectangle            maMarkedObjRect;
     tools::Rectangle            maMarkedObjRectNoOffset;
     tools::Rectangle            maMarkedPointsRect;
@@ -294,7 +301,8 @@ public:
     // Mark all objects within a rectangular area
     // Just objects are marked which are inclosed completely
     void MarkObj(const tools::Rectangle& rRect, bool bUnmark);
-    void MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark=false, bool bImpNoSetMarkHdl=false);
+    void MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark = false, bool bDoNoSetMarkHdl = false,
+                 std::vector<basegfx::B2DRectangle> const & rSubSelections = std::vector<basegfx::B2DRectangle>());
     void MarkAllObj(SdrPageView* pPV=nullptr); // pPage=NULL => all displayed pages
     void UnmarkAllObj(SdrPageView const * pPV=nullptr); // pPage=NULL => all displayed pages
 
diff --git a/svx/source/svdraw/svdmrkv.cxx b/svx/source/svdraw/svdmrkv.cxx
index 4871df93b629..31f2d0373154 100644
--- a/svx/source/svdraw/svdmrkv.cxx
+++ b/svx/source/svdraw/svdmrkv.cxx
@@ -46,6 +46,7 @@
 #include <sdr/overlay/overlayrollingrectangle.hxx>
 #include <svx/sdr/overlay/overlaymanager.hxx>
 #include <svx/sdr/table/tablecontroller.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
 #include <svx/sdr/contact/viewcontact.hxx>
 #include <svx/sdrpaintwindow.hxx>
 #include <svx/sdrpagewindow.hxx>
@@ -130,6 +131,38 @@ void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition
     }
 }
 
+class MarkingSubSelectionOverlay
+{
+    sdr::overlay::OverlayObjectList maObjects;
+
+public:
+    MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
+    {
+        if (comphelper::LibreOfficeKit::isActive())
+            return; // We do client-side object manipulation with the Kit API
+
+        for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+        {
+            SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+            const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
+
+            if (xTargetOverlay.is())
+            {
+                const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer;
+                const Color aHighlightColor = aSvtOptionsDrawinglayer.getHilightColor();
+
+                std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
+                    std::make_unique<sdr::overlay::OverlaySelection>(
+                        sdr::overlay::OverlayType::Transparent,
+                        aHighlightColor, rSelections, false);
+
+                xTargetOverlay->add(*pNew);
+                maObjects.append(std::move(pNew));
+            }
+        }
+    }
+};
+
 
 // MarkView
 
@@ -674,6 +707,8 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
     SdrHdlKind eSaveKind(SdrHdlKind::Move);
     SdrObject* pSaveObj = nullptr;
 
+    mpMarkingSubSelectionOverlay.reset();
+
     if(pSaveOldFocusHdl
         && pSaveOldFocusHdl->GetObj()
         && dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
@@ -774,6 +809,11 @@ void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
         {
             return;
         }
+
+        if (!maSubSelectionList.empty())
+        {
+            mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
+        }
     }
 
     tools::Rectangle aRect(GetMarkedObjRect());
@@ -1900,7 +1940,8 @@ void collectUIInformation(SdrObject* pObj)
 
 }
 
-void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool bImpNoSetMarkHdl)
+void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool bDoNoSetMarkHdl,
+                          std::vector<basegfx::B2DRectangle> const & rSubSelections)
 {
     if (pObj!=nullptr && pPV!=nullptr && IsObjMarkable(pObj, pPV)) {
         BrkAction();
@@ -1917,7 +1958,10 @@ void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool
                 GetMarkedObjectListWriteAccess().DeleteMark(nPos);
             }
         }
-        if (!bImpNoSetMarkHdl) {
+
+        maSubSelectionList = rSubSelections;
+
+        if (!bDoNoSetMarkHdl) {
             MarkListHasChanged();
             AdjustMarkHdl();
         }
commit 5a677ca3715b7e1da3f3a2a79083f06958d97aaa
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Wed May 27 14:27:30 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 13:36:39 2020 +0200

    sd: some style fixes in Outliner.cxx
    
    Also no need to reset the mpImpl in destructor when it will be
    reset and destroyed in the next step anyway.
    
    Change-Id: I5027f962efc4159e61aa7eda26619db2e3b9434c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95309
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 43a6def48f425361d79dff46dca41685d5ee03fa)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95920
    (cherry picked from commit 76ba829bf18123daf552afe253a8304a982ceb8d)

diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index 42267b3f50e3..ef8204ad5b80 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -228,7 +228,6 @@ SdOutliner::SdOutliner( SdDrawDocument* pDoc, OutlinerMode nMode )
 /// Nothing spectacular in the destructor.
 SdOutliner::~SdOutliner()
 {
-    mpImpl.reset();
 }
 
 /** Prepare find&replace or spellchecking.  This distinguishes between three
@@ -484,7 +483,9 @@ bool SdOutliner::StartSearchAndReplace (const SvxSearchItem* pSearchItem)
 
         const SvxSearchCmd nCommand (mpSearchItem->GetCommand());
         if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL)
+        {
             bEndOfSearch = SearchAndReplaceAll ();
+        }
         else
         {
             RememberStartPosition ();
@@ -590,6 +591,7 @@ void SdOutliner::Initialize (bool bDirectionIsForward)
 bool SdOutliner::SearchAndReplaceAll()
 {
     bool bRet = true;
+
     // Save the current position to be restored after having replaced all
     // matches.
     RememberStartPosition ();
@@ -773,13 +775,13 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti
         pOutlinerView = mpImpl->GetOutlinerView();
     }
 
-    if (pViewShell != nullptr)
+    if (pViewShell)
     {
         mpView = pViewShell->GetView();
         mpWindow = pViewShell->GetActiveWindow();
         pOutlinerView->SetWindow(mpWindow);
 
-        if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ))
+        if (nullptr != dynamic_cast<const sd::DrawViewShell*>(pViewShell.get()))
         {
             // When replacing we first check if there is a selection
             // indicating a match.  If there is then replace it.  The
@@ -802,7 +804,7 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti
             {
                 ProvideNextTextObject ();
 
-                if ( ! mbEndOfSearch)
+                if (!mbEndOfSearch)
                 {
                     // Remember the current position as the last one with a
                     // text object.
@@ -824,7 +826,7 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti
                 }
             }
         }
-        else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ))
+        else if (nullptr != dynamic_cast<const sd::OutlineViewShell*>(pViewShell.get()))
         {
             mpDrawDocument->GetDocSh()->SetWaitCursor(false);
             // The following loop is executed more than once only when a
commit 81d9bdd146c2e9c5e522c8f5e5cafc2b2ad42721
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Wed May 27 14:20:44 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 13:36:12 2020 +0200

    sd: in Outline add getViewShellBase to simplify code
    
    This simplifies constant casting of SfxViewShell::Current to
    sd::ViewShellBase. Instead of doing it in every method, let's
    have a dedicated function for that.
    
    Change-Id: Iadbe446c7edce5df40c2da14e3eee65012dd4d77
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95308
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 29e06da2b63441f4458c482eae1cd36bd10b4728)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95919
    (cherry picked from commit 7824545abeb308a89c08aa98e20901df05934625)

diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index c592f715a07f..42267b3f50e3 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -126,6 +126,16 @@ private:
     OutlinerView* mpOutlineView;
 };
 
+namespace
+{
+
+sd::ViewShellBase* getViewShellBase()
+{
+    return dynamic_cast<sd::ViewShellBase*>(SfxViewShell::Current());
+}
+
+} // end anonymous namespace
+
 SdOutliner::SdOutliner( SdDrawDocument* pDoc, OutlinerMode nMode )
     : SdrOutliner( &pDoc->GetItemPool(), nMode ),
       mpImpl(new Implementation()),
@@ -245,7 +255,7 @@ void SdOutliner::PrepareSpelling()
 {
     mbPrepareSpellingPending = false;
 
-    sd::ViewShellBase* pBase = dynamic_cast< sd::ViewShellBase *>( SfxViewShell::Current() );
+    sd::ViewShellBase* pBase = getViewShellBase();
     if (pBase != nullptr)
         SetViewShell (pBase->GetMainViewShell());
     SetRefDevice( SD_MOD()->GetVirtualRefDevice() );
@@ -287,7 +297,7 @@ void SdOutliner::EndSpelling()
     std::shared_ptr<sd::ViewShell> pViewShell (mpWeakViewShell.lock());
     std::shared_ptr<sd::ViewShell> pOldViewShell (pViewShell);
 
-    sd::ViewShellBase* pBase = dynamic_cast< sd::ViewShellBase *>( SfxViewShell::Current() );
+    sd::ViewShellBase* pBase = getViewShellBase();
     if (pBase != nullptr)
         pViewShell = pBase->GetMainViewShell();
     else
@@ -430,7 +440,7 @@ bool SdOutliner::StartSearchAndReplace (const SvxSearchItem* pSearchItem)
     mpDrawDocument->GetDocSh()->SetWaitCursor( true );
     if (mbPrepareSpellingPending)
         PrepareSpelling();
-    sd::ViewShellBase* pBase = dynamic_cast< sd::ViewShellBase *>( SfxViewShell::Current() );
+    sd::ViewShellBase* pBase = getViewShellBase();
     // Determine whether we have to abort the search.  This is necessary
     // when the main view shell does not support searching.
     bool bAbort = false;
@@ -1631,7 +1641,7 @@ void SdOutliner::BeginConversion()
 {
     SetRefDevice( SD_MOD()->GetVirtualRefDevice() );
 
-    sd::ViewShellBase* pBase = dynamic_cast<sd::ViewShellBase*>( SfxViewShell::Current() );
+    sd::ViewShellBase* pBase = getViewShellBase();
     if (pBase != nullptr)
         SetViewShell (pBase->GetMainViewShell());
 
commit 906529637866059b1c639844c3de337b824782e6
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Wed May 27 14:13:02 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 13:35:53 2020 +0200

    sd: move IsValidTextObject into source as anon. function
    
    IsValidTextObject is only used internally in Outline, so put it
    inside the source file as an anonymous function and not as a
    member function of Outliner.
    
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95307
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 14b55b3a8de070935489acd4f6f8be7b6a3d96a4)
    
    Change-Id: I867dc4f465a694e61b6102b19f8e54ce623e2858
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95918
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 5bc2314a95199337e796de9be23650a71dff9294)

diff --git a/sd/inc/Outliner.hxx b/sd/inc/Outliner.hxx
index b368f03dab1f..5617ae62da11 100644
--- a/sd/inc/Outliner.hxx
+++ b/sd/inc/Outliner.hxx
@@ -409,13 +409,6 @@ private:
     */
     bool ShowWrapArroundDialog();
 
-    /** Check whether the object pointed to by the iterator is a valid text
-        object.
-        @param aPosition
-            The object for which to test whether it is a valid text object.
-    */
-    static bool IsValidTextObject (const ::sd::outliner::IteratorPosition& rPosition);
-
     /** Put text of current text object into outliner so that the text can
         be searched/spell checked.
     */
diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index 21ea70fbb0d9..c592f715a07f 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -1041,6 +1041,18 @@ void SdOutliner::RestoreStartPosition()
     }
 }
 
+namespace
+{
+
+bool lclIsValidTextObject(const sd::outliner::IteratorPosition& rPosition)
+{
+    auto* pObject = dynamic_cast< SdrTextObj* >( rPosition.mxObject.get() );
+    return (pObject != nullptr) && pObject->HasText() && ! pObject->IsEmptyPresObj();
+}
+
+} // end anonymous namespace
+
+
 /** The main purpose of this method is to iterate over all shape objects of
     the search area (current selection, current view, or whole document)
     until a text object has been found that contains at least one match or
@@ -1086,7 +1098,7 @@ void SdOutliner::ProvideNextTextObject()
             bool bForbiddenPage = comphelper::LibreOfficeKit::isActive() && (maCurrentPosition.mePageKind != PageKind::Standard || maCurrentPosition.meEditMode != EditMode::Page);
 
             // Switch to the current object only if it is a valid text object.
-            if (!bForbiddenPage && IsValidTextObject(maCurrentPosition))
+            if (!bForbiddenPage && lclIsValidTextObject(maCurrentPosition))
             {
                 // Don't set yet in case of searching: the text object may not match.
                 if (meMode != SEARCH)
@@ -1254,12 +1266,6 @@ bool SdOutliner::ShowWrapArroundDialog()
     return (nBoxResult == RET_YES);
 }
 
-bool SdOutliner::IsValidTextObject (const sd::outliner::IteratorPosition& rPosition)
-{
-    SdrTextObj* pObject = dynamic_cast< SdrTextObj* >( rPosition.mxObject.get() );
-    return (pObject != nullptr) && pObject->HasText() && ! pObject->IsEmptyPresObj();
-}
-
 void SdOutliner::PutTextIntoOutliner()
 {
     mpSearchSpellTextObj = dynamic_cast<SdrTextObj*>( mpObj );
commit 6ee21e14fe548954e784c4d50566b04c641df51e
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Wed May 27 14:07:10 2020 +0200
Commit:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
CommitDate: Thu Jul 30 13:35:36 2020 +0200

    sd: move LOK search result logic to it's own method
    
    Change-Id: I67cbe4d3d63bffdab72c09b3a956f67806588348
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95306
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit e9dbdc9fc6ff943650e6e18986ed1cce913971ef)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95917
    Tested-by: Tomaž Vajngerl <quikee at gmail.com>
    (cherry picked from commit 6eec4e40f6f8ea88d3136295d5485ee73ef7bf2c)

diff --git a/sd/inc/Outliner.hxx b/sd/inc/Outliner.hxx
index 78a32cbb1e83..b368f03dab1f 100644
--- a/sd/inc/Outliner.hxx
+++ b/sd/inc/Outliner.hxx
@@ -353,6 +353,10 @@ private:
     */
     bool SearchAndReplaceOnce(std::vector<::sd::SearchSelection>* pSelections = nullptr);
 
+    void sendLOKSearchResultCallback(std::shared_ptr<sd::ViewShell>& pViewShell,
+                                     OutlinerView* pOutlinerView,
+                                     std::vector<sd::SearchSelection>* pSelections);
+
     /** Detect changes of the document or view and react accordingly.  Such
         changes may occur because different calls to
         <member>SearchAndReplace()</member> there usually is user
diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx
index 20c248a78fd0..21ea70fbb0d9 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -690,6 +690,66 @@ bool SdOutliner::SearchAndReplaceAll()
     return bRet;
 }
 
+void SdOutliner::sendLOKSearchResultCallback(std::shared_ptr<sd::ViewShell> & pViewShell,
+                                             OutlinerView* pOutlinerView,
+                                             std::vector<sd::SearchSelection>* pSelections)
+{
+    std::vector<::tools::Rectangle> aLogicRects;
+    pOutlinerView->GetSelectionRectangles(aLogicRects);
+
+    // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this
+    // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles
+    // which makes that method unusable for others
+    if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit())
+    {
+        for (tools::Rectangle& rRectangle : aLogicRects)
+        {
+            rRectangle = OutputDevice::LogicToLogic(rRectangle, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip));
+        }
+    }
+
+    std::vector<OString> aLogicRectStrings;
+    std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings),
+        [](const ::tools::Rectangle& rRectangle)
+    {
+        return rRectangle.toString();
+    });
+
+    OString sRectangles = comphelper::string::join("; ", aLogicRectStrings);
+
+    if (!pSelections)
+    {
+        // notify LibreOfficeKit about changed page
+        OString aPayload = OString::number(maCurrentPosition.mnPageIndex);
+        SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase();
+        rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr());
+
+        // also about search result selections
+        boost::property_tree::ptree aTree;
+        aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr());
+        aTree.put("highlightAll", false);
+
+        boost::property_tree::ptree aChildren;
+        boost::property_tree::ptree aChild;
+        aChild.put("part", OString::number(maCurrentPosition.mnPageIndex).getStr());

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list