diff --git a/editeng/CppunitTest_editeng_editeng.mk b/editeng/CppunitTest_editeng_editeng.mk new file mode 100644 index 000000000000..38cbc9543db6 --- /dev/null +++ b/editeng/CppunitTest_editeng_editeng.mk @@ -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: diff --git a/editeng/Module_editeng.mk b/editeng/Module_editeng.mk index 6fedbcb6043e..fca6e8426c4e 100644 --- a/editeng/Module_editeng.mk +++ b/editeng/Module_editeng.mk @@ -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 \ diff --git a/editeng/qa/editeng/editeng.cxx b/editeng/qa/editeng/editeng.cxx new file mode 100644 index 000000000000..5c1f23e7e389 --- /dev/null +++ b/editeng/qa/editeng/editeng.cxx @@ -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 + +#include + +#include +#include +#include +#include + +#include +#include + +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 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 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 xData + = aEditEngine.CreateTransferable(ESelection(0, 0, 0, aText.getLength())); + + // Then make sure the RTF result doesn't contain the style: + auto pData = dynamic_cast(xData.get()); + SvMemoryStream& rStream = pData->GetRTFStream(); + tools::SvRef 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: */ diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index a1086a8cab93..9f465f4d9d5d 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -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, diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx index 83c495072ab8..61c37b00d96f 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -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 ); diff --git a/editeng/source/editeng/impedit4.cxx b/editeng/source/editeng/impedit4.cxx index 5b72524270b8..13f13b6fe491 100644 --- a/editeng/source/editeng/impedit4.cxx +++ b/editeng/source/editeng/impedit4.cxx @@ -77,6 +77,7 @@ #include #include #include +#include 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 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( + GetStyleSheetPool()->Find(rParent, pParaStyle->GetFamily())); + if (pParent) + { + aUsedParagraphStyles.insert(pParent); + } + } + + const OUString& rFollow = pParaStyle->GetFollow(); + if (!rFollow.isEmpty()) + { + auto pFollow = static_cast( + 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 );