sw/inc/cmdid.h                                       |    1 
 sw/inc/unoprnms.hxx                                  |    1 
 sw/qa/core/unocore/unocore.cxx                       |   42 ++++++++++++++++
 sw/source/core/unocore/unomap.cxx                    |    1 
 sw/source/core/unocore/unotext.cxx                   |   47 +++++++++++++++++++
 sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx |   37 +++-----------
 6 files changed, 100 insertions(+), 29 deletions(-)

New commits:
commit cfd3b14fd4e1ee889dd356523e5cdaa639786d37
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Aug 2 08:23:45 2024 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Aug 2 10:37:31 2024 +0200

    tdf#162295 DOCX import: optimize the check if the header/footer is empty
    
    The original DOCX bugdoc has some 813 shapes and 150 sections, which
    means that isContentEmpty() in the domain mapper is a significant cost.
    
    We don't have layout at import time, so checking if a shape has its
    anchor in this header/footer is still expensive, but using internal sw
    API makes the 2 other checks constant and the shape check is faster this
    way.
    
    - old cost: 16638 ms
    
    - new cost: 11910 ms (72% of baseline)
    
    Change-Id: I22f75ef82b0e2f618e425e076377eea3640b83cc
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171390
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/inc/cmdid.h b/sw/inc/cmdid.h
index bd36f0bdd3b3..1e2cc3d10991 100644
--- a/sw/inc/cmdid.h
+++ b/sw/inc/cmdid.h
@@ -678,6 +678,7 @@ class SwUINumRuleItem;
 #define FN_UNO_GRAPHIC_PREVIEW              (FN_EXTRA2 + 130)
 #define FN_UNO_LINEBREAK (FN_EXTRA2 + 131)
 #define FN_UNO_CONTENT_CONTROL (FN_EXTRA2 + 132)
+#define FN_UNO_IS_CONTENT_EMPTY  (FN_EXTRA2 + 133)
 
 // Area: Help
 // Region: Traveling & Selection
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index 7ed25b929247..e3d7e073c24d 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -939,6 +939,7 @@ inline constexpr OUString UNO_NAME_DATE_STRING = 
u"DateString"_ustr;
 inline constexpr OUString UNO_NAME_PARA_ID = u"ParaId"_ustr;
 inline constexpr OUString UNO_NAME_PARA_ID_PARENT = u"ParaIdParent"_ustr;
 inline constexpr OUString UNO_NAME_CONTENT_CONTROL_TYPE = 
u"ContentControlType"_ustr;
+inline constexpr OUString UNO_NAME_IS_CONTENT_EMPTY = u"IsContentEmpty"_ustr;
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index 83f8605f3920..0d60108cb2f3 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -32,6 +32,7 @@
 #include <textcontentcontrol.hxx>
 #include <frmmgr.hxx>
 #include <fmtcntnt.hxx>
+#include <strings.hrc>
 
 using namespace ::com::sun::star;
 
@@ -1085,6 +1086,47 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, 
testWrapTextAtFlyStart)
     CPPUNIT_ASSERT(bWrapTextAtFlyStart);
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testEmptyHeader)
+{
+    // Empty header: IsContentEmpty is true.
+    createSwDoc();
+    uno::Sequence<beans::PropertyValue> aInsertArgs
+        = { comphelper::makePropertyValue(u"PageStyle"_ustr, 
SwResId(STR_POOLPAGE_STANDARD)) };
+    dispatchCommand(mxComponent, u".uno:InsertPageHeader"_ustr, aInsertArgs);
+    uno::Reference<beans::XPropertySet> 
xPageStyle(getStyles("PageStyles")->getByName("Standard"),
+                                                   uno::UNO_QUERY);
+    uno::Reference<beans::XPropertySet> 
xHeaderText(xPageStyle->getPropertyValue("HeaderText"),
+                                                    uno::UNO_QUERY);
+    bool bIsContentEmpty = false;
+    xHeaderText->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty;
+    CPPUNIT_ASSERT(bIsContentEmpty);
+
+    // Header has 1 paragraph with text: IsContentEmpty is false.
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    pWrtShell->Insert("text");
+    bIsContentEmpty = true;
+    xHeaderText->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty;
+    CPPUNIT_ASSERT(!bIsContentEmpty);
+
+    // Header has 2 paragraphs: IsContentEmpty is false.
+    pWrtShell->SelAll();
+    pWrtShell->DelRight();
+    pWrtShell->SplitNode();
+    bIsContentEmpty = true;
+    xHeaderText->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty;
+    CPPUNIT_ASSERT(!bIsContentEmpty);
+
+    // Header has an anchoed object: IsContentEmpty is false.
+    pWrtShell->SelAll();
+    pWrtShell->DelRight();
+    SwFlyFrameAttrMgr aMgr(true, pWrtShell, Frmmgr_Type::TEXT, nullptr);
+    RndStdIds eAnchor = RndStdIds::FLY_AT_PARA;
+    aMgr.InsertFlyFrame(eAnchor, aMgr.GetPos(), aMgr.GetSize());
+    bIsContentEmpty = true;
+    xHeaderText->getPropertyValue("IsContentEmpty") >>= bIsContentEmpty;
+    CPPUNIT_ASSERT(!bIsContentEmpty);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unomap.cxx 
b/sw/source/core/unocore/unomap.cxx
index 55e1632c57e3..977028ceb9de 100644
--- a/sw/source/core/unocore/unomap.cxx
+++ b/sw/source/core/unocore/unomap.cxx
@@ -1369,6 +1369,7 @@ std::span<const SfxItemPropertyMapEntry> 
SwUnoPropertyMapProvider::GetPropertyMa
                 static SfxItemPropertyMapEntry const aTextMap[] =
                 {
                     REDLINE_NODE_PROPERTIES
+                    { UNO_NAME_IS_CONTENT_EMPTY, FN_UNO_IS_CONTENT_EMPTY, 
cppu::UnoType<bool>::get(), 
PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
                 };
                 m_aMapEntriesArr[nPropertyId] = aTextMap;
             }
diff --git a/sw/source/core/unocore/unotext.cxx 
b/sw/source/core/unocore/unotext.cxx
index e569b4c40f18..de9789faf3eb 100644
--- a/sw/source/core/unocore/unotext.cxx
+++ b/sw/source/core/unocore/unotext.cxx
@@ -1032,6 +1032,53 @@ SwXText::getPropertyValue(
             }
         }
         break;
+        case FN_UNO_IS_CONTENT_EMPTY:
+        {
+            const SwStartNode* pStartNode = GetStartNode();
+            SwNodeOffset nStartIndex = pStartNode->GetIndex();
+            SwNodeOffset nEndIndex = pStartNode->EndOfSectionIndex();
+            if (nEndIndex - nStartIndex > SwNodeOffset(2))
+            {
+                // More than 1 node between the start and end one: not empty.
+                aRet <<= false;
+                return aRet;
+            }
+
+            if (nEndIndex - nStartIndex == SwNodeOffset(2))
+            {
+                SwPaM aPaM(*pStartNode);
+                aPaM.Move(fnMoveForward, GoInNode);
+                SwTextNode* pTextNode = aPaM.Start()->GetNode().GetTextNode();
+                if (pTextNode && !pTextNode->GetText().isEmpty())
+                {
+                    // 1 node, but that text node has text: not empty.
+                    aRet <<= false;
+                    return aRet;
+                }
+            }
+
+            sw::FrameFormats<sw::SpzFrameFormat*>& rFormats = 
*GetDoc()->GetSpzFrameFormats();
+            for(sw::SpzFrameFormat* pFormat: rFormats)
+            {
+                const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+                const SwNode* pAnchorNode = rAnchor.GetAnchorNode();
+                if (!pAnchorNode)
+                {
+                    continue;
+                }
+
+                SwNodeOffset nAnchorIndex = pAnchorNode->GetIndex();
+                if (nAnchorIndex > nStartIndex && nAnchorIndex < nEndIndex)
+                {
+                    // This fly or draw format has an anchor in the node 
section: not empty.
+                    aRet <<= false;
+                    return aRet;
+                }
+            }
+            // Otherwise empty.
+            aRet <<= true;
+        }
+        break;
     }
     return aRet;
 }
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 3a4307228a8f..344dbe261341 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -3791,41 +3791,20 @@ void 
DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop, b
 namespace
 {
 // Determines if the XText content is empty (no text, no shapes, no tables)
-bool isContentEmpty(uno::Reference<text::XText> const& xText, 
uno::Reference<text::XTextDocument> const& xTextDocument)
+bool isContentEmpty(uno::Reference<text::XText> const& xText)
 {
     if (!xText.is())
         return true; // no XText means it's empty
 
-    uno::Reference<drawing::XDrawPageSupplier> 
xDrawPageSupplier(xTextDocument, uno::UNO_QUERY);
-    auto xDrawPage = xDrawPageSupplier->getDrawPage();
-    if (xDrawPage && xDrawPage->hasElements())
+    uno::Reference<beans::XPropertySet> xTextProperties(xText, uno::UNO_QUERY);
+    if (!xTextProperties.is())
     {
-        for (sal_Int32 i = 0, nCount = xDrawPage->getCount(); i < nCount; ++i)
-        {
-            uno::Reference<text::XTextContent> 
xShape(xDrawPage->getByIndex(i), uno::UNO_QUERY);
-            if (xShape.is())
-            {
-                uno::Reference<text::XTextRange> xAnchor = xShape->getAnchor();
-                if (xAnchor.is() && xAnchor->getText() == xText)
-                    return false;
-            }
-        }
+        return true;
     }
 
-    uno::Reference<container::XEnumerationAccess> 
xEnumAccess(xText->getText(), uno::UNO_QUERY);
-    uno::Reference<container::XEnumeration> xEnum = 
xEnumAccess->createEnumeration();
-    while (xEnum->hasMoreElements())
-    {
-        auto xObject = xEnum->nextElement();
-        uno::Reference<text::XTextTable> const xTextTable(xObject, 
uno::UNO_QUERY);
-        if (xTextTable.is())
-            return false;
-
-        uno::Reference<text::XTextRange> const xParagraph(xObject, 
uno::UNO_QUERY);
-        if (xParagraph.is() && !xParagraph->getString().isEmpty())
-            return false;
-    }
-    return true;
+    bool bContentEmpty{};
+    xTextProperties->getPropertyValue("IsContentEmpty") >>= bContentEmpty;
+    return bContentEmpty;
 }
 
 } // end anonymous namespace
@@ -3969,7 +3948,7 @@ void 
DomainMapper_Impl::checkIfHeaderFooterIsEmpty(PagePartType ePagePartType, P
     if (!xPageStyle.is())
         return;
 
-    bool bEmpty = isContentEmpty(m_aTextAppendStack.top().xTextAppend, 
GetTextDocument());
+    bool bEmpty = isContentEmpty(m_aTextAppendStack.top().xTextAppend);
 
     if (eType == PageType::FIRST && bEmpty)
     {

Reply via email to