sw/qa/writerfilter/dmapper/DomainMapper.cxx          |   40 +++++++++++++++++++
 sw/qa/writerfilter/dmapper/data/clipboard.rtf        |    7 +++
 sw/source/writerfilter/dmapper/DomainMapper.cxx      |   32 +++++++++++++++
 sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx |    4 +
 sw/source/writerfilter/dmapper/StyleSheetTable.cxx   |   30 ++++++++++++++
 sw/source/writerfilter/dmapper/StyleSheetTable.hxx   |    4 +
 6 files changed, 117 insertions(+)

New commits:
commit f58e3e3402c87755a2dd3cb83f29d00c40b94f1a
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Jun 24 15:46:03 2024 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Jun 24 19:09:09 2024 +0200

    Related: tdf#161652 sw, RTF paste: only keep used paragraph styles
    
    When pasting from old enough Impress that doesn't have commit
    afb4ea67463d9f0200dc6216cfd932aec0984c82 (tdf#161652 editeng, RTF copy:
    only write used paragraph styles, 2024-06-20), it still happened that we
    got many styles from an Impress slide's paragraph in Writer than just
    the style of that paragraph itself.
    
    The problem is that if we want to avoid problems with bad user input,
    that has to be handled on the RTF paste / import side, not on the
    producing side.
    
    Fix the problem by filtering out unused paragraph styles also on the RTF
    import (paste) side, in the IsNewDoc() case, which is the clipboard case
    (not RTF file open).
    
    With this, we attempt to filter out not needed paragraph styles both on
    the import and export side.
    
    Change-Id: Ic2c63e5f45245bb4296ec0d1a95558c459667e29
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169445
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/writerfilter/dmapper/DomainMapper.cxx 
b/sw/qa/writerfilter/dmapper/DomainMapper.cxx
index 3e7acdd3b495..05e6029c8c21 100644
--- a/sw/qa/writerfilter/dmapper/DomainMapper.cxx
+++ b/sw/qa/writerfilter/dmapper/DomainMapper.cxx
@@ -14,8 +14,13 @@
 #include <com/sun/star/beans/PropertyValues.hpp>
 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
 #include <com/sun/star/text/XTextTable.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
 
 #include <tools/UnitConversion.hxx>
+#include <unotools/streamwrap.hxx>
+#include <comphelper/propertyvalue.hxx>
 
 using namespace ::com::sun::star;
 
@@ -185,6 +190,41 @@ CPPUNIT_TEST_FIXTURE(Test, testTableStyleParaBorder)
     // i.e. the 0 para border distance was applied on the cell instead.
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(203), nLeftBorderDistance);
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testRTFStylePaste)
+{
+    // Given an empty Writer document:
+    mxComponent
+        = loadFromDesktop(u"private:factory/swriter"_ustr, 
u"com.sun.star.text.TextDocument"_ustr);
+
+    // When pasting RTF that has unreferenced paragraph styles:
+    uno::Reference<text::XTextDocument> xTextDocument(mxComponent, 
uno::UNO_QUERY);
+    uno::Reference<text::XTextRange> xText = xTextDocument->getText();
+    uno::Reference<text::XTextRange> xBodyEnd = xText->getEnd();
+    uno::Reference<document::XFilter> xFilter(
+        
m_xSFactory->createInstance(u"com.sun.star.comp.Writer.RtfFilter"_ustr), 
uno::UNO_QUERY);
+    uno::Reference<document::XImporter> xImporter(xFilter, uno::UNO_QUERY);
+    xImporter->setTargetDocument(mxComponent);
+    std::unique_ptr<SvStream> pStream(
+        new SvFileStream(createFileURL(u"clipboard.rtf"), StreamMode::READ));
+    uno::Reference<io::XStream> xStream(new 
utl::OStreamWrapper(std::move(pStream)));
+    uno::Sequence aDescriptor{ 
comphelper::makePropertyValue(u"InputStream"_ustr, xStream),
+                               
comphelper::makePropertyValue(u"InsertMode"_ustr, true),
+                               
comphelper::makePropertyValue(u"TextInsertModeRange"_ustr,
+                                                             xBodyEnd) };
+    CPPUNIT_ASSERT(xFilter->filter(aDescriptor));
+
+    // Then make sure those paragraph styles don't show up in the past result:
+    uno::Reference<style::XStyleFamiliesSupplier> 
xStyleFamiliesSupplier(mxComponent,
+                                                                         
uno::UNO_QUERY);
+    uno::Reference<container::XNameAccess> xStyleFamilies
+        = xStyleFamiliesSupplier->getStyleFamilies();
+    uno::Reference<container::XNameAccess> xStyleFamily(
+        xStyleFamilies->getByName(u"ParagraphStyles"_ustr), uno::UNO_QUERY);
+    // Without the accompanying fix in place, this test would have failed, 
'Default Drawing Style'
+    // was imported, even if no pasted content referenced it.
+    CPPUNIT_ASSERT(!xStyleFamily->hasByName(u"Default Drawing Style"_ustr));
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/writerfilter/dmapper/data/clipboard.rtf 
b/sw/qa/writerfilter/dmapper/data/clipboard.rtf
new file mode 100644
index 000000000000..0ebaa3aee0e9
--- /dev/null
+++ b/sw/qa/writerfilter/dmapper/data/clipboard.rtf
@@ -0,0 +1,7 @@
+{ tfnsi
+{\stylesheet
+{\s1 Default Drawing Style;}
+{\s37 Beehive~LT~Titel;}
+}
+\s37 beehive\par
+}
diff --git a/sw/source/writerfilter/dmapper/DomainMapper.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper.cxx
index 8e32f2e34569..adb29ba57c23 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper.cxx
@@ -2700,7 +2700,39 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const 
PropertyMapPtr& rContext )
         const OUString sConvertedStyleName = 
pStyleTable->ConvertStyleNameExt(sStringValue);
         m_pImpl->SetCurrentParaStyleName( sConvertedStyleName );
         if (m_pImpl->GetTopContext() && m_pImpl->GetTopContextType() != 
CONTEXT_SECTION)
+        {
             m_pImpl->GetTopContext()->Insert( PROP_PARA_STYLE_NAME, uno::Any( 
sConvertedStyleName ));
+
+            if (!m_pImpl->IsNewDoc())
+            {
+                // Mark the paragraph style & its parent / follow as used.
+                pStyleTable->MarkParagraphStyleAsUsed(sConvertedStyleName);
+
+                auto pStyle = 
pStyleTable->FindStyleSheetByConvertedStyleName(sConvertedStyleName);
+                if (pStyle)
+                {
+                    if (!pStyle->m_sBaseStyleIdentifier.isEmpty())
+                    {
+                        StyleSheetEntryPtr pParent = 
pStyleTable->FindStyleSheetByISTD(pStyle->m_sBaseStyleIdentifier);
+                        if (pParent)
+                        {
+                            OUString sParent = 
StyleSheetTable::ConvertStyleName(pParent->m_sStyleName).first;
+                            pStyleTable->MarkParagraphStyleAsUsed(sParent);
+                        }
+                    }
+
+                    if (!pStyle->m_sNextStyleIdentifier.isEmpty())
+                    {
+                        StyleSheetEntryPtr pFollow = 
pStyleTable->FindStyleSheetByISTD(pStyle->m_sNextStyleIdentifier);
+                        if (pFollow)
+                        {
+                            OUString sFollow = 
StyleSheetTable::ConvertStyleName(pFollow->m_sStyleName).first;
+                            pStyleTable->MarkParagraphStyleAsUsed(sFollow);
+                        }
+                    }
+                }
+            }
+        }
     }
     break;
     case NS_ooxml::LN_EG_RPrBase_rStyle:
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 47ef07610ca7..8bb8da5b1db1 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -435,6 +435,10 @@ DomainMapper_Impl::~DomainMapper_Impl()
         RemoveLastParagraph();
         
suppress_fun_call_w_exception(GetStyleSheetTable()->ApplyClonedTOCStyles());
     }
+    else if (m_pStyleSheetTable)
+    {
+        m_pStyleSheetTable->RemoveUnusedParagraphStyles();
+    }
     if (hasTableManager())
     {
         getTableManager().endLevel();
diff --git a/sw/source/writerfilter/dmapper/StyleSheetTable.cxx 
b/sw/source/writerfilter/dmapper/StyleSheetTable.cxx
index 29f50ecbcf66..5c259de5ff38 100644
--- a/sw/source/writerfilter/dmapper/StyleSheetTable.cxx
+++ b/sw/source/writerfilter/dmapper/StyleSheetTable.cxx
@@ -285,6 +285,8 @@ struct StyleSheetTable_Impl
     std::vector< ListCharStylePropertyMap_t > m_aListCharStylePropertyVector;
     bool                                    m_bHasImportedDefaultParaProps;
     bool                                    m_bIsNewDoc;
+    std::set<OUString> m_aInsertedParagraphStyles;
+    std::set<OUString> m_aUsedParagraphStyles;
 
     StyleSheetTable_Impl(DomainMapper& rDMapper, 
rtl::Reference<SwXTextDocument> xTextDocument, bool bIsNewDoc);
 
@@ -1422,6 +1424,13 @@ void StyleSheetTable::ApplyStyleSheetsImpl(const 
FontTablePtr& rFontTable, std::
                             aMissingParent.emplace_back( sParentStyle, xStyle 
);
 
                         xStyles->insertByName( sConvertedStyleName, uno::Any( 
xStyle) );
+
+                        if (!m_pImpl->m_bIsNewDoc && bParaStyle)
+                        {
+                            // Remember the inserted style, which may or may 
not be referred during
+                            // pasting content.
+                            
m_pImpl->m_aInsertedParagraphStyles.insert(sConvertedStyleName);
+                        }
                     }
 
                     beans::PropertyValues aGrabBag = 
pEntry->GetInteropGrabBagSeq();
@@ -2165,6 +2174,27 @@ OUString StyleSheetTable::getOrCreateCharStyle( 
PropertyValueVector_t& rCharProp
     return sListLabel;
 }
 
+void StyleSheetTable::MarkParagraphStyleAsUsed(const OUString& rName)
+{
+    m_pImpl->m_aUsedParagraphStyles.insert(rName);
+}
+
+void StyleSheetTable::RemoveUnusedParagraphStyles()
+{
+    uno::Reference< container::XNameAccess > xStyleFamilies = 
m_pImpl->m_xTextDocument->getStyleFamilies();
+    uno::Reference<container::XNameContainer> xParaStyles;
+    xStyleFamilies->getByName(getPropertyName(PROP_PARAGRAPH_STYLES)) >>= 
xParaStyles;
+    for (const auto& rName : m_pImpl->m_aInsertedParagraphStyles)
+    {
+        if (m_pImpl->m_aUsedParagraphStyles.contains(rName))
+        {
+            continue;
+        }
+
+        xParaStyles->removeByName(rName);
+    }
+}
+
 }//namespace writerfilter
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/writerfilter/dmapper/StyleSheetTable.hxx 
b/sw/source/writerfilter/dmapper/StyleSheetTable.hxx
index d388032cdb46..fc2d2a0ba8fa 100644
--- a/sw/source/writerfilter/dmapper/StyleSheetTable.hxx
+++ b/sw/source/writerfilter/dmapper/StyleSheetTable.hxx
@@ -114,6 +114,10 @@ public:
 
     const StyleSheetEntryPtr & GetCurrentEntry() const;
 
+    void MarkParagraphStyleAsUsed(const OUString& rName);
+    /// In case of pasting, removes unused paragraph styles, inserted during 
the paste.
+    void RemoveUnusedParagraphStyles();
+
 private:
     // Properties
     virtual void lcl_attribute(Id Name, Value & val) override;

Reply via email to