sw/inc/unotextbodyhf.hxx | 2 sw/inc/unotextrange.hxx | 3 sw/qa/extras/ooxmlexport/data/tdf143384_tableInFoot_negativeMargins.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport11.cxx | 7 + sw/source/core/unocore/unoframe.cxx | 8 + sw/source/core/unocore/unoobj2.cxx | 15 ++ sw/source/core/unocore/unotext.cxx | 56 +++++++--- writerfilter/source/dmapper/DomainMapper_Impl.cxx | 8 + writerfilter/source/dmapper/PropertyIds.cxx | 1 writerfilter/source/dmapper/PropertyIds.hxx | 1 10 files changed, 83 insertions(+), 18 deletions(-)
New commits: commit c1ad429d925855c1baacbdeca1ef42f4486eb9c2 Author: Attila Szűcs <szucs.atti...@nisz.hu> AuthorDate: Tue Jul 27 11:45:58 2021 +0200 Commit: László Németh <nem...@numbertext.org> CommitDate: Wed Aug 25 13:45:23 2021 +0200 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 <nem...@numbertext.org> Reviewed-by: László Németh <nem...@numbertext.org> diff --git a/sw/inc/unotextbodyhf.hxx b/sw/inc/unotextbodyhf.hxx index fbccc982ef04..99887c6beb5c 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 f3ed06fb4a06..4d8ed2df2c06 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 000000000000..918c1029fb4a Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf143384_tableInFoot_negativeMargins.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx index de3eea4c0659..08e0cbd9f8f7 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 74d366d9961d..8b9250671d78 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 2103220de62b..e99450378bac 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 7e0c73d8c778..fa779d95abd8 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) - { - rUnoCursor.GetPoint()->nNode = *pTableNode->EndOfSectionNode(); - pCont = GetDoc()->GetNodes().GoNext(&rUnoCursor.GetPoint()->nNode); - pTableNode = pCont->FindTableNode(); - } - if (pCont) + + if (!bIgnoreTables) { - 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 8fffee9d32e4..c6c423ea6517 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 c3e7cbaa2de9..00d5b795b132 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 f1bef16da581..8e23533c57ba 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.