tdf#158556: provide objects anchored to node as a hidden property
This introduces a hidden property of SwXParagraph, named
OOXMLImport_AnchoredShapes.
Testing on my system, starting the main process first, then launching
another one like
time soffice path/to/bugdoc.docx
... so that it only measures time spent in import, gave the following
figures:
LibreOffice 7.5.0.3 (TDF build):
real 1m49.016s
user 0m0.000s
sys 0m0.000s
LibreOffice 7.6.0.3 (TDF build):
real 8m37.386s
user 0m0.000s
sys 0m0.000s
Current master (my no-debug build):
real 10m6.776s
user 0m0.000s
sys 0m0.000s
Current master with this patch (my no-debug build):
real 5m41.524s
user 0m0.000s
sys 0m0.015s
Indeed, it is not as fast as it used to be; and the fix doesn't really
remove the quadratic complexity, just uses faster iteration. If there
is a way to directly list objects anchored to a given paragraph, rather
than iterating over all objects checking their anchors, that would get
much faster, but that would be a rather large change.
Change-Id: Ie50515815e85fdce498d065185199c9b31d95794
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160813
Tested-by: Mike Kaganski <mike.kaganski@collabora.com>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
diff --git a/sw/source/core/inc/unoparaframeenum.hxx b/sw/source/core/inc/unoparaframeenum.hxx
index 81a653a..c44e0f6 100644
--- a/sw/source/core/inc/unoparaframeenum.hxx
+++ b/sw/source/core/inc/unoparaframeenum.hxx
@@ -31,6 +31,8 @@ class SwNodeIndex;
class SwPaM;
class SwFrameFormat;
namespace com { namespace sun { namespace star { namespace text { class XTextContent; } } } }
namespace sw
{
struct FrameClient final : public SwClient
@@ -70,6 +72,8 @@ struct SwXParaFrameEnumeration
static rtl::Reference<SwXParaFrameEnumeration> Create(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat = nullptr);
};
css::uno::Reference<css::text::XTextContent> FrameClientToXTextContent(sw::FrameClient* pClient);
#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOPARAFRAMEENUM_HXX
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoobj2.cxx b/sw/source/core/unocore/unoobj2.cxx
index e4f5ccd..7614f0a 100644
--- a/sw/source/core/unocore/unoobj2.cxx
+++ b/sw/source/core/unocore/unoobj2.cxx
@@ -1788,9 +1788,17 @@ bool SwXParaFrameEnumerationImpl::CreateNextObject()
if (m_vFrames.empty())
return false;
SwFrameFormat* const pFormat = static_cast<SwFrameFormat*>(
m_vFrames.front()->GetRegisteredIn());
m_xNextObject.set(FrameClientToXTextContent(m_vFrames.front().get()));
m_vFrames.pop_front();
return m_xNextObject.is();
}
uno::Reference<text::XTextContent> FrameClientToXTextContent(sw::FrameClient* pClient)
{
assert(pClient);
uno::Reference<text::XTextContent> xRet;
SwFrameFormat* const pFormat = static_cast<SwFrameFormat*>(pClient->GetRegisteredIn());
// the format should be valid here, otherwise the client
// would have been removed by PurgeFrameClients
// check for a shape first
@@ -1799,33 +1807,32 @@ bool SwXParaFrameEnumerationImpl::CreateNextObject()
SdrObject* pObject(nullptr);
pFormat->CallSwClientNotify(sw::FindSdrObjectHint(pObject));
if(pObject)
m_xNextObject.set(pObject->getUnoShape(), uno::UNO_QUERY);
xRet.set(pObject->getUnoShape(), uno::UNO_QUERY);
}
else
{
const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
OSL_ENSURE(pIdx, "where is the index?");
SwNode const*const pNd =
m_pUnoCursor->GetDoc().GetNodes()[ pIdx->GetIndex() + 1 ];
SwNode const* const pNd = pIdx->GetNodes()[pIdx->GetIndex() + 1];
if (!pNd->IsNoTextNode())
{
m_xNextObject = static_cast<SwXFrame*>(SwXTextFrame::CreateXTextFrame(
xRet = static_cast<SwXFrame*>(SwXTextFrame::CreateXTextFrame(
*pFormat->GetDoc(), pFormat).get());
}
else if (pNd->IsGrfNode())
{
m_xNextObject.set(SwXTextGraphicObject::CreateXTextGraphicObject(
xRet.set(SwXTextGraphicObject::CreateXTextGraphicObject(
*pFormat->GetDoc(), pFormat));
}
else
{
assert(pNd->IsOLENode());
m_xNextObject.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
xRet.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
*pFormat->GetDoc(), pFormat));
}
}
return m_xNextObject.is();
return xRet;
}
sal_Bool SAL_CALL
diff --git a/sw/source/core/unocore/unoparagraph.cxx b/sw/source/core/unocore/unoparagraph.cxx
index 68dbe34..dab5270 100644
--- a/sw/source/core/unocore/unoparagraph.cxx
+++ b/sw/source/core/unocore/unoparagraph.cxx
@@ -54,6 +54,7 @@
#include <com/sun/star/drawing/BitmapMode.hpp>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/servicehelper.hxx>
#include <editeng/unoipset.hxx>
#include <svl/listener.hxx>
@@ -550,6 +551,23 @@ uno::Sequence< uno::Any > SwXParagraph::Impl::GetPropertyValues_Impl(
continue;
}
if (pPropertyNames[nProp] == "OOXMLImport_AnchoredShapes")
{
// A hack to provide list of anchored objects fast
// See reanchorObjects in writerfilter/source/dmapper/DomainMapper_Impl.cxx
FrameClientSortList_t aFrames;
CollectFrameAtNode(rTextNode, aFrames, false); // Frames anchored to paragraph
CollectFrameAtNode(rTextNode, aFrames, true); // Frames anchored to character
std::vector<uno::Reference<text::XTextContent>> aRet;
aRet.reserve(aFrames.size());
for (const auto& rFrame : aFrames)
if (auto xContent = FrameClientToXTextContent(rFrame.pFrameClient.get()))
aRet.push_back(xContent);
pValues[nProp] <<= comphelper::containerToSequence(aRet);
continue;
}
SfxItemPropertyMapEntry const*const pEntry =
rMap.getByName( pPropertyNames[nProp] );
if (!pEntry)
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 679a1a3..3a0c5a6 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -750,6 +750,59 @@ void DomainMapper_Impl::AddDummyParaForTableInSection()
return sName;
}
static void reanchorObjects(const uno::Reference<uno::XInterface>& xFrom,
const uno::Reference<text::XTextRange>& xTo,
const uno::Reference<drawing::XDrawPage>& xDrawPage)
{
std::vector<uno::Reference<text::XTextContent>> aShapes;
bool bFastPathDone = false;
if (uno::Reference<beans::XPropertySet> xProps{ xFrom, uno::UNO_QUERY })
{
try
{
// See SwXParagraph::Impl::GetPropertyValues_Impl
uno::Sequence<uno::Reference<text::XTextContent>> aSeq;
xProps->getPropertyValue(u"OOXMLImport_AnchoredShapes"_ustr) >>= aSeq;
aShapes.insert(aShapes.end(), aSeq.begin(), aSeq.end());
bFastPathDone = true;
}
catch (const uno::Exception&)
{
}
}
if (!bFastPathDone)
{
// Can this happen? Fallback to slow DrawPage iteration and range comparison
uno::Reference<text::XTextRange> xRange(xFrom, uno::UNO_QUERY_THROW);
uno::Reference<text::XTextRangeCompare> xCompare(xRange->getText(), uno::UNO_QUERY_THROW);
const sal_Int32 count = xDrawPage->getCount();
for (sal_Int32 i = 0; i < count; ++i)
{
try
{
uno::Reference<text::XTextContent> xShape(xDrawPage->getByIndex(i),
uno::UNO_QUERY_THROW);
uno::Reference<text::XTextRange> xAnchor(xShape->getAnchor(), uno::UNO_SET_THROW);
if (xCompare->compareRegionStarts(xAnchor, xRange) <= 0
&& xCompare->compareRegionEnds(xAnchor, xRange) >= 0)
{
aShapes.push_back(xShape);
}
}
catch (const uno::Exception&)
{
// Can happen e.g. in compareRegion*, when the shape is in a header,
// and paragraph in body
}
}
}
for (const auto& xShape : aShapes)
xShape->attach(xTo);
}
void DomainMapper_Impl::RemoveLastParagraph( )
{
if (m_bDiscardHeaderFooter)
@@ -828,31 +881,7 @@ void DomainMapper_Impl::RemoveLastParagraph( )
auto xEnumeration = xEA->createEnumeration();
uno::Reference<text::XTextRange> xPrevParagraph(xEnumeration->nextElement(),
uno::UNO_QUERY_THROW);
uno::Reference<text::XTextRange> xParaRange(xParagraph, uno::UNO_QUERY_THROW);
uno::Reference<text::XTextRangeCompare> xRegionCompare(xParaRange->getText(),
uno::UNO_QUERY_THROW);
const sal_Int32 count = xDrawPage->getCount();
for (sal_Int32 i = 0; i < count; ++i)
{
try
{
uno::Reference<text::XTextContent> xShape(xDrawPage->getByIndex(i),
uno::UNO_QUERY_THROW);
uno::Reference<text::XTextRange> xAnchor(xShape->getAnchor(),
uno::UNO_SET_THROW);
if (xRegionCompare->compareRegionStarts(xAnchor, xParaRange) <= 0
&& xRegionCompare->compareRegionEnds(xAnchor, xParaRange) >= 0)
{
xShape->attach(xPrevParagraph);
}
}
catch (const uno::Exception&)
{
// Can happen e.g. in compareRegion*, when the shape is in a header,
// and paragraph in body
}
}
reanchorObjects(xParagraph, xPrevParagraph, xDrawPage);
}
xParagraph->dispose();