tdf#161652 editeng, RTF copy: only write used paragraph styles

Copy a single world from the Impress bugdoc to Writer, number of
paragraph styles increase from 126 to 221, while only +0 or +1 are
expected.

It seems the problem is that the editeng doc of the shape refers to all
styles of the masterpage and we write the style table before the
content, so we export all styles to be on the safe side.

Fix the problem by iterating the paragraphs of the selection in the
"copy" (not "export") case, assuming that typically the selection
doesn't refer to all available styles in the document, and the number of
paragraphs in a shape is not a large amount.

An alternative would be to limit the style import on the RTF reading
side, but not producing those not needed styles in the first place looks
superior.

Change-Id: I43e4c542e530ff6422357a28399718e89fdbabe9
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169251
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
This commit is contained in:
Miklos Vajna 2024-06-20 09:53:00 +02:00
parent 41d26f14ff
commit afb4ea6746
6 changed files with 225 additions and 4 deletions

View file

@ -0,0 +1,69 @@
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
#*************************************************************************
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
#*************************************************************************
$(eval $(call gb_CppunitTest_CppunitTest,editeng_editeng))
$(eval $(call gb_CppunitTest_add_exception_objects,editeng_editeng, \
editeng/qa/editeng/editeng \
))
$(eval $(call gb_CppunitTest_use_library_objects,editeng_editeng,editeng))
$(eval $(call gb_CppunitTest_use_libraries,editeng_editeng, \
basegfx \
comphelper \
cppu \
cppuhelper \
docmodel \
i18nlangtag \
i18nutil \
lng \
sal \
salhelper \
sax \
sot \
sfx \
svl \
svt \
test \
tk \
tl \
ucbhelper \
unotest \
utl \
vcl \
xo \
))
$(eval $(call gb_CppunitTest_use_externals,editeng_editeng,\
boost_headers \
icuuc \
libxml2 \
))
$(eval $(call gb_CppunitTest_set_include,editeng_editeng,\
-I$(SRCDIR)/editeng/inc \
-I$(SRCDIR)/editeng/source/editeng \
$$(INCLUDE) \
))
$(eval $(call gb_CppunitTest_use_sdk_api,editeng_editeng))
$(eval $(call gb_CppunitTest_use_ure,editeng_editeng))
$(eval $(call gb_CppunitTest_use_vcl,editeng_editeng))
$(eval $(call gb_CppunitTest_use_rdb,editeng_editeng,services))
$(eval $(call gb_CppunitTest_use_configuration,editeng_editeng))
$(eval $(call gb_CppunitTest_use_more_fonts,editeng_editeng))
# vim: set noet sw=4 ts=4:

View file

@ -31,6 +31,7 @@ $(eval $(call gb_Module_add_l10n_targets,editeng,\
$(eval $(call gb_Module_add_check_targets,editeng,\
$(if $(and $(filter $(COM),MSC),$(MERGELIBS)),, \
CppunitTest_editeng_editeng \
CppunitTest_editeng_core) \
CppunitTest_editeng_borderline \
CppunitTest_editeng_lookuptree \

View file

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <test/bootstrapfixture.hxx>
#include <memory>
#include <editeng/editeng.hxx>
#include <sfx2/app.hxx>
#include <svtools/parrtf.hxx>
#include <svtools/rtftoken.h>
#include <editdoc.hxx>
#include <eeobj.hxx>
using namespace com::sun::star;
namespace
{
/// Covers editeng/source/editeng/ fixes.
class Test : public test::BootstrapFixture
{
public:
Test() {}
void setUp() override
{
test::BootstrapFixture::setUp();
mpItemPool = new EditEngineItemPool();
SfxApplication::GetOrCreate();
}
void tearDown() override
{
mpItemPool.clear();
test::BootstrapFixture::tearDown();
}
protected:
rtl::Reference<EditEngineItemPool> mpItemPool;
};
/// RTF parser that counts the styles in the document.
class StyleCounter : public SvRTFParser
{
public:
StyleCounter(SvStream& rStream);
void NextToken(int nToken) override;
int m_nStyles = 0;
};
StyleCounter::StyleCounter(SvStream& rStream)
: SvRTFParser(rStream)
{
}
void StyleCounter::NextToken(int nToken)
{
if (nToken == RTF_S)
{
++m_nStyles;
}
}
CPPUNIT_TEST_FIXTURE(Test, testRTFStyleExport)
{
// Given a document with an unreferenced style:
EditEngine aEditEngine(mpItemPool.get());
rtl::Reference<SfxStyleSheetPool> xStyles(new SfxStyleSheetPool(*mpItemPool));
xStyles->Make("mystyle", SfxStyleFamily::Para);
aEditEngine.SetStyleSheetPool(xStyles.get());
OUString aText = u"mytest"_ustr;
aEditEngine.SetText(aText);
// When copying a word from that document:
uno::Reference<datatransfer::XTransferable> xData
= aEditEngine.CreateTransferable(ESelection(0, 0, 0, aText.getLength()));
// Then make sure the RTF result doesn't contain the style:
auto pData = dynamic_cast<EditDataObject*>(xData.get());
SvMemoryStream& rStream = pData->GetRTFStream();
tools::SvRef<StyleCounter> xReader(new StyleCounter(rStream));
CPPUNIT_ASSERT(xReader->CallParser() != SvParserState::Error);
// Without the accompanying fix in place, this test would have failed with:
// - Expected: 0
// - Actual : 1
// i.e. unreferenced paragraph styles were exported.
CPPUNIT_ASSERT_EQUAL(0, xReader->m_nStyles);
}
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View file

@ -770,7 +770,7 @@ private:
EditPaM ReadXML( SvStream& rInput, EditSelection aSel );
EditPaM ReadHTML( SvStream& rInput, const OUString& rBaseURL, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs );
ErrCode WriteText( SvStream& rOutput, EditSelection aSel );
ErrCode WriteRTF( SvStream& rOutput, EditSelection aSel );
ErrCode WriteRTF( SvStream& rOutput, EditSelection aSel, bool bClipboard );
void WriteXML(SvStream& rOutput, const EditSelection& rSel);
void WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_Int32 nPara, sal_Int32 nPos,

View file

@ -3951,7 +3951,7 @@ uno::Reference< datatransfer::XTransferable > ImpEditEngine::CreateTransferable(
pDataObj->GetString() = convertLineEnd(GetSelected(aSelection), GetSystemLineEnd()); // System specific
WriteRTF( pDataObj->GetRTFStream(), aSelection );
WriteRTF( pDataObj->GetRTFStream(), aSelection, /*bClipboard=*/true );
pDataObj->GetRTFStream().Seek( 0 );
WriteXML( pDataObj->GetODFStream(), aSelection );

View file

@ -77,6 +77,7 @@
#include <memory>
#include <unordered_map>
#include <vector>
#include <set>
using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
@ -206,7 +207,7 @@ void ImpEditEngine::Write(SvStream& rOutput, EETextFormat eFormat, const EditSel
if ( eFormat == EETextFormat::Text )
WriteText( rOutput, rSel );
else if ( eFormat == EETextFormat::Rtf )
WriteRTF( rOutput, rSel );
WriteRTF( rOutput, rSel, /*bClipboard=*/false );
else if ( eFormat == EETextFormat::Xml )
WriteXML( rOutput, rSel );
else if ( eFormat == EETextFormat::Html )
@ -291,7 +292,7 @@ void ImpEditEngine::WriteXML(SvStream& rOutput, const EditSelection& rSel)
SvxWriteXML( *GetEditEnginePtr(), rOutput, aESel );
}
ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel )
ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel, bool bClipboard )
{
assert( IsUpdateLayout() && "WriteRTF for UpdateMode = sal_False!" );
CheckIdleFormatter();
@ -456,6 +457,50 @@ ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel )
nId++;
}
// Collect used paragraph styles when copying to the clipboard.
std::set<SfxStyleSheetBase*> aUsedParagraphStyles;
if (bClipboard)
{
for (sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++)
{
ContentNode* pNode = maEditDoc.GetObject(nNode);
if (!pNode)
{
continue;
}
SfxStyleSheet* pParaStyle = pNode->GetStyleSheet();
if (!pParaStyle)
{
continue;
}
aUsedParagraphStyles.insert(pParaStyle);
const OUString& rParent = pParaStyle->GetParent();
if (!rParent.isEmpty())
{
auto pParent = static_cast<SfxStyleSheet*>(
GetStyleSheetPool()->Find(rParent, pParaStyle->GetFamily()));
if (pParent)
{
aUsedParagraphStyles.insert(pParent);
}
}
const OUString& rFollow = pParaStyle->GetFollow();
if (!rFollow.isEmpty())
{
auto pFollow = static_cast<SfxStyleSheet*>(
GetStyleSheetPool()->Find(rFollow, pParaStyle->GetFamily()));
if (pFollow)
{
aUsedParagraphStyles.insert(pFollow);
}
}
}
}
if ( aSSSIterator->Count() )
{
@ -465,6 +510,11 @@ ErrCode ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel )
for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle;
pStyle = aSSSIterator->Next() )
{
if (bClipboard && !aUsedParagraphStyles.contains(pStyle))
{
// Don't write unused paragraph styles in the clipboard case.
continue;
}
rOutput << endl;
rOutput.WriteChar( '{' ).WriteOString( OOO_STRING_SVTOOLS_RTF_S );