tdf#163883 editeng RTF export: fix broken offsets into the para style table

Regression from commit 70d1bd6ee0
(tdf#161652 editeng, RTF copy: only write used paragraph styles,
2024-06-20), type a few characters into the title placeholder shape in
Impress, switch to the Outline view, select all, copy, paste into
Writer: the resulting text nodes won't have paragraph styles, just
direct formatting.

Inspecting the produced RTF, the problem is that the styles table
started to omit unused styles, but the style -> offset mapping table
still included them, so the offset didn't match, which results in losing
the style on RTF import.

Fix the problem by building the mapping table later also also ignoring
unused paragraph styles there, to restore consistency between declaring
style entries and referring to them.

Change-Id: I9156a881543cc710eb12990f110ceefc77a1858b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/176747
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
This commit is contained in:
Miklos Vajna 2024-11-19 08:20:26 +01:00
parent 04afe8b86c
commit c8b607b7c0
2 changed files with 48 additions and 13 deletions

View file

@ -9,8 +9,6 @@
#include <test/bootstrapfixture.hxx>
#include <memory>
#include <editeng/editeng.hxx>
#include <sfx2/app.hxx>
#include <svtools/parrtf.hxx>
@ -54,6 +52,7 @@ public:
void NextToken(int nToken) override;
int m_nStyles = 0;
std::vector<int> m_aStyleValues;
};
StyleCounter::StyleCounter(SvStream& rStream)
@ -66,6 +65,7 @@ void StyleCounter::NextToken(int nToken)
if (nToken == RTF_S)
{
++m_nStyles;
m_aStyleValues.push_back(nTokenValue);
}
}
@ -94,6 +94,36 @@ CPPUNIT_TEST_FIXTURE(Test, testRTFStyleExport)
// i.e. unreferenced paragraph styles were exported.
CPPUNIT_ASSERT_EQUAL(0, xReader->m_nStyles);
}
CPPUNIT_TEST_FIXTURE(Test, testRTFStyleExportReferToStyle)
{
// Given a document with one unused and one used style:
EditEngine aEditEngine(mpItemPool.get());
rtl::Reference<SfxStyleSheetPool> xStyles(new SfxStyleSheetPool(*mpItemPool));
xStyles->Make("mystyle", SfxStyleFamily::Para);
xStyles->Make("mystyle2", SfxStyleFamily::Para);
auto pStyle = static_cast<SfxStyleSheet*>(xStyles->Find("mystyle2", SfxStyleFamily::Para));
aEditEngine.SetStyleSheetPool(xStyles.get());
OUString aText = u"mytest"_ustr;
aEditEngine.SetText(aText);
aEditEngine.SetStyleSheet(0, pStyle);
// When copying a word from that document:
uno::Reference<datatransfer::XTransferable> xData
= aEditEngine.CreateTransferable(ESelection(0, 0, 0, aText.getLength()));
// Then make sure the declared and referred style indexes for the used style match:
auto pData = dynamic_cast<EditDataObject*>(xData.get());
SvMemoryStream& rStream = pData->GetRTFStream();
tools::SvRef<StyleCounter> xReader(new StyleCounter(rStream));
CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), xReader->m_aStyleValues.size());
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 1
// - Actual : 2
// i.e. \s2 was used to refer to \s1, so the paragraph style was lost.
CPPUNIT_ASSERT_EQUAL(xReader->m_aStyleValues[0], xReader->m_aStyleValues[1]);
}
}
CPPUNIT_PLUGIN_IMPLEMENT();

View file

@ -444,17 +444,6 @@ ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel, bool bCl
// StyleSheets...
if ( GetStyleSheetPool() )
{
std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(GetStyleSheetPool(),
SfxStyleFamily::All);
// fill aStyleSheetToIdMap
sal_uInt32 nId = 1;
for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
pStyle = aSSSIterator->Next() )
{
aStyleSheetToIdMap[pStyle] = nId;
nId++;
}
// Collect used paragraph styles when copying to the clipboard.
std::set<SfxStyleSheetBase*> aUsedParagraphStyles;
if (bClipboard)
@ -499,6 +488,22 @@ ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel, bool bCl
}
}
std::shared_ptr<SfxStyleSheetIterator> aSSSIterator = std::make_shared<SfxStyleSheetIterator>(GetStyleSheetPool(),
SfxStyleFamily::All);
// fill aStyleSheetToIdMap
sal_uInt32 nId = 1;
for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
pStyle = aSSSIterator->Next() )
{
if (bClipboard && !aUsedParagraphStyles.contains(pStyle))
{
// Don't include unused paragraph styles in the clipboard case.
continue;
}
aStyleSheetToIdMap[pStyle] = nId;
nId++;
}
if ( aSSSIterator->Count() )
{