tdf#143384 DOCX import: fix SAXException at header with table
Regression from commit d656191ec308d4280b93c7169372e543a255d108
"tdf#119952 DOCX import: fix negative page margins".
Add SwXHeadFootText::CreateTextCursor(bool bIgnoreTables = false)
(modeled after SwXBodyText::CreateTextCursor) to create text cursor
for copying the header/footer also started with table during
fly frame creation in convertoToTextFrame().
Note: add hidden property PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF
to use the new feature in domainmapper (followed
commit af4e5ee0f93c1ff442d08caed5c875f2b2c1fd43
"tdf#97128 DOCX import: fix frame direction").
Co-authored-by: Tibor Nagy (NISZ)
Change-Id: I96e2cf2dddcecd146c53c12d7fdb44fc4d90fa0d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/119549
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/inc/unotextbodyhf.hxx b/sw/inc/unotextbodyhf.hxx
index fbccc98..99887c6 100644
--- a/sw/inc/unotextbodyhf.hxx
+++ b/sw/inc/unotextbodyhf.hxx
@@ -115,6 +115,8 @@ public:
static css::uno::Reference< css::text::XText >
CreateXHeadFootText(SwFrameFormat & rHeadFootFormat, const bool bIsHeader);
css::uno::Reference<css::text::XTextCursor> CreateTextCursor(const bool bIgnoreTables = false);
// XInterface
virtual css::uno::Any SAL_CALL queryInterface(
const css::uno::Type& rType) override;
diff --git a/sw/inc/unotextrange.hxx b/sw/inc/unotextrange.hxx
index f3ed06f..4d8ed2d 100644
--- a/sw/inc/unotextrange.hxx
+++ b/sw/inc/unotextrange.hxx
@@ -57,7 +57,8 @@ namespace sw {
enum class TextRangeMode {
RequireTextNode,
AllowNonTextNode
AllowNonTextNode,
AllowTableNode
};
void DeepCopyPaM(SwPaM const & rSource, SwPaM & rTarget);
diff --git a/sw/qa/extras/ooxmlexport/data/tdf143384_tableInFoot_negativeMargins.docx b/sw/qa/extras/ooxmlexport/data/tdf143384_tableInFoot_negativeMargins.docx
new file mode 100644
index 0000000..918c102
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf143384_tableInFoot_negativeMargins.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index de3eea4..08e0cbd 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -1618,6 +1618,13 @@ DECLARE_OOXMLEXPORT_TEST(testTdf119952_negativeMargins, "tdf119952_negativeMargi
CPPUNIT_ASSERT_EQUAL(OUString(" aaaa bbbb cccc dddd eeee"), parseDump("/root/page[3]/header/txt/anchored/fly"));
}
DECLARE_OOXMLEXPORT_TEST(testTdf143384_tableInFoot_negativeMargins, "tdf143384_tableInFoot_negativeMargins.docx")
{
// There should be no crash during loading of the document
// so, let's check just how much pages we have
CPPUNIT_ASSERT_EQUAL(1, getPages());
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx
index 74d366d..8b925067 100644
--- a/sw/source/core/unocore/unoframe.cxx
+++ b/sw/source/core/unocore/unoframe.cxx
@@ -1418,7 +1418,13 @@ void SwXFrame::setPropertyValue(const OUString& rPropertyName, const ::uno::Any&
const ::SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
if (!pEntry)
throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
{
// Hack to skip the dummy CursorNotIgnoreTables property
if (rPropertyName == "CursorNotIgnoreTables")
return;
else
throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast <cppu::OWeakObject*> (this));
}
const sal_uInt8 nMemberId(pEntry->nMemberId);
uno::Any aValue(_rValue);
diff --git a/sw/source/core/unocore/unoobj2.cxx b/sw/source/core/unocore/unoobj2.cxx
index 2103220..e994503 100644
--- a/sw/source/core/unocore/unoobj2.cxx
+++ b/sw/source/core/unocore/unoobj2.cxx
@@ -1116,6 +1116,7 @@ bool XTextRangeToSwPaM( SwUnoInternalPaM & rToFill,
SwXTextPortion* pPortion = nullptr;
SwXText* pText = nullptr;
SwXParagraph* pPara = nullptr;
SwXHeadFootText* pHeadText = nullptr;
if(xRangeTunnel.is())
{
pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel);
@@ -1125,12 +1126,26 @@ bool XTextRangeToSwPaM( SwUnoInternalPaM & rToFill,
::sw::UnoTunnelGetImplementation<SwXTextPortion>(xRangeTunnel);
pText = ::sw::UnoTunnelGetImplementation<SwXText>(xRangeTunnel);
pPara = ::sw::UnoTunnelGetImplementation<SwXParagraph>(xRangeTunnel);
if (eMode == TextRangeMode::AllowTableNode)
pHeadText = dynamic_cast<SwXHeadFootText*>(pText);
}
// if it's a text then create a temporary cursor there and re-use
// the pCursor variable
// #i108489#: Reference in outside scope to keep cursor alive
uno::Reference< text::XTextCursor > xTextCursor;
if (pHeadText)
{
// if it is a header / footer text, and eMode == TextRangeMode::AllowTableNode
// then set the cursor to the beginning of the text
// if it is started with a table then set into the table
xTextCursor.set(pHeadText->CreateTextCursor(true));
xTextCursor->gotoEnd(true);
pCursor =
comphelper::getUnoTunnelImplementation<OTextCursorHelper>(xTextCursor);
pCursor->GetPaM()->Normalize();
}
else
if (pText)
{
xTextCursor.set( pText->CreateCursor() );
diff --git a/sw/source/core/unocore/unotext.cxx b/sw/source/core/unocore/unotext.cxx
index 7e0c73d..fa779d9 100644
--- a/sw/source/core/unocore/unotext.cxx
+++ b/sw/source/core/unocore/unotext.cxx
@@ -1537,14 +1537,31 @@ SwXText::convertToTextFrame(
{
throw uno::RuntimeException();
}
// tdf#143384 recognize dummy property, that was set to make createTextCursor
// to not ignore tables.
// It is enough to use this hack only for the range start,
// because as far as I know, the range cannot end with table when this property is set.
::sw::TextRangeMode eMode = ::sw::TextRangeMode::RequireTextNode;
for (const auto& rCellProperty : rFrameProperties)
{
if (rCellProperty.Name == "CursorNotIgnoreTables")
{
bool bAllowNonTextNode = false;
rCellProperty.Value >>= bAllowNonTextNode;
if (bAllowNonTextNode)
eMode = ::sw::TextRangeMode::AllowTableNode;
break;
}
}
uno::Reference< text::XTextContent > xRet;
std::optional<SwUnoInternalPaM> pTempStartPam(*GetDoc());
std::optional<SwUnoInternalPaM> pEndPam(*GetDoc());
if (!::sw::XTextRangeToSwPaM(*pTempStartPam, xStart) ||
!::sw::XTextRangeToSwPaM(*pEndPam, xEnd))
if (!::sw::XTextRangeToSwPaM(*pTempStartPam, xStart, eMode)
|| !::sw::XTextRangeToSwPaM(*pEndPam, xEnd))
{
throw lang::IllegalArgumentException();
}
auto pStartPam(GetDoc()->CreateUnoCursor(*pTempStartPam->GetPoint()));
if (pTempStartPam->HasMark())
{
@@ -2613,8 +2630,7 @@ uno::Any SAL_CALL SwXHeadFootText::queryInterface(const uno::Type& rType)
: ret;
}
uno::Reference<text::XTextCursor> SAL_CALL
SwXHeadFootText::createTextCursor()
uno::Reference<text::XTextCursor> SwXHeadFootText::CreateTextCursor(const bool bIgnoreTables)
{
SolarMutexGuard aGuard;
@@ -2632,18 +2648,22 @@ SwXHeadFootText::createTextCursor()
// after the table - otherwise the cursor would be in the body text!
SwStartNode const*const pOwnStartNode = rNode.FindSttNodeByType(
(m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
// is there a table here?
SwTableNode* pTableNode = rUnoCursor.GetNode().FindTableNode();
SwContentNode* pCont = nullptr;
while (pTableNode)
if (!bIgnoreTables)
{
rUnoCursor.GetPoint()->nNode = *pTableNode->EndOfSectionNode();
pCont = GetDoc()->GetNodes().GoNext(&rUnoCursor.GetPoint()->nNode);
pTableNode = pCont->FindTableNode();
}
if (pCont)
{
rUnoCursor.GetPoint()->nContent.Assign(pCont, 0);
// is there a table here?
SwTableNode* pTableNode = rUnoCursor.GetNode().FindTableNode();
SwContentNode* pCont = nullptr;
while (pTableNode)
{
rUnoCursor.GetPoint()->nNode = *pTableNode->EndOfSectionNode();
pCont = GetDoc()->GetNodes().GoNext(&rUnoCursor.GetPoint()->nNode);
pTableNode = pCont->FindTableNode();
}
if (pCont)
{
rUnoCursor.GetPoint()->nContent.Assign(pCont, 0);
}
}
SwStartNode const*const pNewStartNode = rUnoCursor.GetNode().FindSttNodeByType(
(m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
@@ -2656,6 +2676,12 @@ SwXHeadFootText::createTextCursor()
return static_cast<text::XWordCursor*>(pXCursor.get());
}
uno::Reference<text::XTextCursor> SAL_CALL
SwXHeadFootText::createTextCursor()
{
return CreateTextCursor(false);
}
uno::Reference<text::XTextCursor> SAL_CALL SwXHeadFootText::createTextCursorByRange(
const uno::Reference<text::XTextRange>& xTextPosition)
{
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 8fffee9..c6c423e 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -2742,6 +2742,12 @@ void DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop, b
aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_OPAQUE), false));
aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), text::SizeType::MIN));
aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), text::SizeType::MIN));
// tdf#143384 If the header/footer started with a table, convertToTextFrame could not
// convert the table, because it used createTextCursor() -which ignore tables-
// to set the conversion range.
// This dummy property is set to make convertToTextFrame to use an other CreateTextCursor
// method that can be parameterized to not ignore tables.
aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF), true));
fillEmptyFrameProperties(aFrameProperties, false);
@@ -2751,7 +2757,7 @@ void DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop, b
uno::Reference<text::XTextAppendAndConvert> xBodyText(
xRangeStart->getText(), uno::UNO_QUERY);
xBodyText->convertToTextFrame(xRangeStart, xRangeEnd,
xBodyText->convertToTextFrame(xTextAppend, xRangeEnd,
comphelper::containerToSequence(aFrameProperties));
}
m_aHeaderFooterTextAppendStack.pop();
diff --git a/writerfilter/source/dmapper/PropertyIds.cxx b/writerfilter/source/dmapper/PropertyIds.cxx
index c3e7cba..00d5b79 100644
--- a/writerfilter/source/dmapper/PropertyIds.cxx
+++ b/writerfilter/source/dmapper/PropertyIds.cxx
@@ -364,6 +364,7 @@ OUString getPropertyName( PropertyIds eId )
case PROP_RTL_GUTTER:
sName = "RtlGutter";
break;
case PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF: sName = "CursorNotIgnoreTables"; break;
}
assert(sName.getLength()>0);
return sName;
diff --git a/writerfilter/source/dmapper/PropertyIds.hxx b/writerfilter/source/dmapper/PropertyIds.hxx
index f1bef16..8e23533 100644
--- a/writerfilter/source/dmapper/PropertyIds.hxx
+++ b/writerfilter/source/dmapper/PropertyIds.hxx
@@ -361,6 +361,7 @@ enum PropertyIds
,PROP_CELL_FORMULA_CONVERTED
,PROP_GUTTER_MARGIN
,PROP_RTL_GUTTER
,PROP_CURSOR_NOT_IGNORE_TABLES_IN_HF
};
//Returns the UNO string equivalent to eId.