editeng/CppunitTest_editeng_editeng.mk |   69 ++++++++++++++++++++++
 editeng/Module_editeng.mk              |    1 
 editeng/qa/editeng/editeng.cxx         |  101 +++++++++++++++++++++++++++++++++
 editeng/source/editeng/impedit.hxx     |    2 
 editeng/source/editeng/impedit2.cxx    |    2 
 editeng/source/editeng/impedit4.cxx    |   54 ++++++++++++++++-
 6 files changed, 225 insertions(+), 4 deletions(-)

New commits:
commit afb4ea67463d9f0200dc6216cfd932aec0984c82
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Jun 20 09:53:00 2024 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Jun 20 10:56:44 2024 +0200

    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 <vmik...@collabora.com>

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 <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: */
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 <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 );

Reply via email to