From cd1536dfd8f0dbc5d1ecf57115774b6bb0dc6754 Mon Sep 17 00:00:00 2001 From: Mike Kaganski Date: Wed, 26 Aug 2026 15:49:51 +0500 Subject: [PATCH] Related: tdf#167713 sw: keep a replayed shape out of the row buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converting a document whose header holds a table with a shape in a cell aborted the RTF import in a Debug CODA build: C:\PROGRA~1\MICROS~1\18\COMMUN~1\VC\Tools\MSVC\1451~1.362\Include\deque(1128) : Assertion failed: back() called on empty deque reached, as crashtesting found it, with COConvert.exe rtf fdo35334-3.odt COConvert.exe pdf fdo35334-3.rtf on https://bugs.documentfoundation.org/attachment.cgi?id=57398. The row buffer is replayed while the states still point at it, so the shape insertion was buffered again, behind the cell being replayed, and dropped with the buffer; the EndShape next to it then closed a shape that was never opened, taking the table manager that the next substream needed. Regression from 6c7c171d1468765f7e2a1872d7243f6bf809e37e (tdf#167713 sw: keep an RTF pict shape in its cell, 2026-08-06). Signed-off-by: Mike Kaganski Change-Id: Ib4fe247910cf67a8b60d448da75510bcf2251885 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/10084 Reviewed-by: Caolán McNamara Tested-by: Jenkins CPCI (cherry picked from commit b2a7dd57076573ddac7141b99f9c33a68fbe799d) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210363 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- .../rtfimport/data/tdf167713-shapetext.rtf | 33 +++++++++++++++++++ sw/qa/extras/rtfimport/rtfimport.cxx | 19 +++++++++++ .../writerfilter/rtftok/rtfdocumentimpl.cxx | 6 ++++ 3 files changed, 58 insertions(+) create mode 100644 sw/qa/extras/rtfimport/data/tdf167713-shapetext.rtf diff --git a/sw/qa/extras/rtfimport/data/tdf167713-shapetext.rtf b/sw/qa/extras/rtfimport/data/tdf167713-shapetext.rtf new file mode 100644 index 000000000000..90d1861d8c9a --- /dev/null +++ b/sw/qa/extras/rtfimport/data/tdf167713-shapetext.rtf @@ -0,0 +1,33 @@ +{\rtf1\ansi\ansicpg1252\deff0 +{\fonttbl{\f0\froman\fcharset0 Liberation Serif;}} +\paperw11906\paperh16838\margl1440\margr1440\margt1440\margb1440 +\sectd \ltrsect +{\header \trowd \irow0\irowband0\lastrow \ltrrow\trgaph108\trleft0\trwWidth9026\trftsWidth3\trautofit0\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 +\clvertalt\clwWidth3009\clftsWidth3 \cellx3009 +\clvertalt\clwWidth3008\clftsWidth3 \cellx6017 +\clvertalt\clwWidth3009\clftsWidth3 \cellx9026 +\pard\plain \ltrpar\ql \li0\ri0\intbl\f0\fs22 {A1}{\cell } +\pard\plain \ltrpar\ql \li0\ri0\intbl\f0\fs22 {\shp{\*\shpinst\shpleft0\shptop0\shpright1000\shpbottom500\shpbxcolumn\shpbxignore\shpbypara\shpbyignore\shpwr2\shpz0{\sp{\sn shapeType}{\sv 202}}{\sp{\sn fFilled}{\sv 0}}{\sp{\sn fLine}{\sv 0}}{\shptxt \pard\plain \ltrpar\ql \f0\fs22 {in the header}\par}}}{\cell } +\pard\plain \ltrpar\ql \li0\ri0\intbl\f0\fs22 {C1}{\cell } +\trowd \irow0\irowband0\lastrow \ltrrow\trgaph108\trleft0\trwWidth9026\trftsWidth3\trautofit0\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 +\clvertalt\clwWidth3009\clftsWidth3 \cellx3009 +\clvertalt\clwWidth3008\clftsWidth3 \cellx6017 +\clvertalt\clwWidth3009\clftsWidth3 \cellx9026\row +\pard\plain \ltrpar\ql \f0\fs22 +\par} +{\footer \trowd \irow0\irowband0\lastrow \ltrrow\trgaph108\trleft0\trwWidth9026\trftsWidth3\trautofit0\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 +\clvertalt\clwWidth3009\clftsWidth3 \cellx3009 +\clvertalt\clwWidth3008\clftsWidth3 \cellx6017 +\clvertalt\clwWidth3009\clftsWidth3 \cellx9026 +\pard\plain \ltrpar\ql \li0\ri0\intbl\f0\fs22 {A1}{\cell } +\pard\plain \ltrpar\ql \li0\ri0\intbl\f0\fs22 {B1}{\cell } +\pard\plain \ltrpar\ql \li0\ri0\intbl\f0\fs22 {\shp{\*\shpinst\shpleft0\shptop0\shpright1000\shpbottom500\shpbxcolumn\shpbxignore\shpbypara\shpbyignore\shpwr2\shpz0{\sp{\sn shapeType}{\sv 202}}{\sp{\sn fFilled}{\sv 0}}{\sp{\sn fLine}{\sv 0}}{\shptxt \pard\plain \ltrpar\ql \f0\fs22 {in the footer}\par}}}{\cell } +\trowd \irow0\irowband0\lastrow \ltrrow\trgaph108\trleft0\trwWidth9026\trftsWidth3\trautofit0\trpaddl108\trpaddr108\trpaddfl3\trpaddfr3\tblind0\tblindtype3 +\clvertalt\clwWidth3009\clftsWidth3 \cellx3009 +\clvertalt\clwWidth3008\clftsWidth3 \cellx6017 +\clvertalt\clwWidth3009\clftsWidth3 \cellx9026\row +\pard\plain \ltrpar\ql \f0\fs22 +\par} +\pard\plain \ltrpar\ql \li0\ri0\f0\fs22 {Body} +\par +} diff --git a/sw/qa/extras/rtfimport/rtfimport.cxx b/sw/qa/extras/rtfimport/rtfimport.cxx index 2e63b0a7dea3..5f6a18881d12 100644 --- a/sw/qa/extras/rtfimport/rtfimport.cxx +++ b/sw/qa/extras/rtfimport/rtfimport.cxx @@ -1042,6 +1042,25 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167713) getProperty(xImage2->getAnchor()->getText(), u"CellName"_ustr)); } +CPPUNIT_TEST_FIXTURE(Test, testTdf167713ShapeText) +{ + // Importing this used to abort in DomainMapper_Impl::substream(), asserting on an empty table + // manager stack: a shape with text in a buffered row was closed without ever being opened, and + // took the manager that the next substream needed. + createSwDoc("tdf167713-shapetext.rtf"); + + // Each shape stayed in the cell it was written in: the footer's, which the draw page hands out + // first, in C1, and the header's in B1. + CPPUNIT_ASSERT_EQUAL(2, getShapes()); + auto xShape1 = getShape(1).queryThrow(); + CPPUNIT_ASSERT_EQUAL(u"C1"_ustr, + getProperty(xShape1->getAnchor()->getText(), u"CellName"_ustr)); + + auto xShape2 = getShape(2).queryThrow(); + CPPUNIT_ASSERT_EQUAL(u"B1"_ustr, + getProperty(xShape2->getAnchor()->getText(), u"CellName"_ustr)); +} + // tests should only be added to rtfIMPORT *if* they fail round-tripping in rtfEXPORT } // end of anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx b/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx index 5934dcec80fe..32dcd9b58e76 100644 --- a/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx +++ b/sw/source/writerfilter/rtftok/rtfdocumentimpl.cxx @@ -1983,7 +1983,13 @@ void RTFDocumentImpl::replayBuffer(RTFBuffer_t& rBuffer, RTFSprms* const pSprms, else if (std::get<0>(aTuple) == RTFBufferTypes::PAR) parBreak(); else if (std::get<0>(aTuple) == RTFBufferTypes::StartShape) + { + // The shape belongs where the replay stands, not in the buffer being replayed. + RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer(); + m_aStates.top().setCurrentBuffer(nullptr); m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), false, RTFSdrImport::SHAPE); + m_aStates.top().setCurrentBuffer(pCurrentBuffer); + } else if (std::get<0>(aTuple) == RTFBufferTypes::ResolveShape) { // Make sure there is no current buffer while replaying the shape,