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

New commits:
commit 6e2f34a4831520d9758ad0862f8c8aec08758f7d
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Jun 24 15:46:03 2024 +0200
Commit:     Mike Kaganski <mike.kagan...@collabora.com>
CommitDate: Wed Jun 26 11:37:43 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.
    
    (cherry picked from commit f58e3e3402c87755a2dd3cb83f29d00c40b94f1a)
    
    Change-Id: Ic2c63e5f45245bb4296ec0d1a95558c459667e29
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169511
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com>

diff --git a/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx 
b/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx
index 6029806e171d..55feedbf2e93 100644
--- a/writerfilter/qa/cppunittests/dmapper/DomainMapper.cxx
+++ b/writerfilter/qa/cppunittests/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/writerfilter/qa/cppunittests/dmapper/data/clipboard.rtf 
b/writerfilter/qa/cppunittests/dmapper/data/clipboard.rtf
new file mode 100644
index 000000000000..0ebaa3aee0e9
--- /dev/null
+++ b/writerfilter/qa/cppunittests/dmapper/data/clipboard.rtf
@@ -0,0 +1,7 @@
+{ tfnsi
+{\stylesheet
+{\s1 Default Drawing Style;}
+{\s37 Beehive~LT~Titel;}
+}
+\s37 beehive\par
+}
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx 
b/writerfilter/source/dmapper/DomainMapper.cxx
index 2b978f921e52..97b4a5a200aa 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -2702,7 +2702,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/writerfilter/source/dmapper/DomainMapper_Impl.cxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 74a4afd186b7..cb5f27b5b9f9 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -442,6 +442,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/writerfilter/source/dmapper/StyleSheetTable.cxx 
b/writerfilter/source/dmapper/StyleSheetTable.cxx
index d932be695b9f..8b6da39037de 100644
--- a/writerfilter/source/dmapper/StyleSheetTable.cxx
+++ b/writerfilter/source/dmapper/StyleSheetTable.cxx
@@ -282,6 +282,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, uno::Reference< 
text::XTextDocument> xTextDocument, bool bIsNewDoc);
 
@@ -1420,6 +1422,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();
@@ -2168,6 +2177,31 @@ 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<style::XStyleFamiliesSupplier> 
xStylesSupplier(m_pImpl->m_xTextDocument,
+                                                                  
uno::UNO_QUERY_THROW);
+    uno::Reference<lang::XMultiServiceFactory> 
xDocFactory(m_pImpl->m_xTextDocument,
+                                                           
uno::UNO_QUERY_THROW);
+    uno::Reference< container::XNameAccess > xStyleFamilies = 
xStylesSupplier->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/writerfilter/source/dmapper/StyleSheetTable.hxx 
b/writerfilter/source/dmapper/StyleSheetTable.hxx
index 92024d80071f..ee79534b9ad9 100644
--- a/writerfilter/source/dmapper/StyleSheetTable.hxx
+++ b/writerfilter/source/dmapper/StyleSheetTable.hxx
@@ -112,6 +112,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