sw/CppunitTest_sw_core_theme.mk | 1 sw/inc/shellio.hxx | 7 + sw/qa/core/theme/ThemeTest.cxx | 189 +++++++++++++++++++++++++++++++++++ sw/qa/core/theme/data/theme_bar.odt |binary sw/qa/core/theme/data/theme_foo.fodt | 39 +++++++ sw/source/filter/basflt/shellio.cxx | 2 sw/source/filter/xml/swxml.cxx | 4 sw/source/uibase/dochdl/swdtflvr.cxx | 1 xmloff/source/style/xmlstyle.cxx | 23 ++++ 9 files changed, 266 insertions(+)
New commits: commit 040528ee50d5dbea71d22edf16578564ac1081ba Author: Mike Kaganski <[email protected]> AuthorDate: Fri Aug 30 23:39:52 2024 +0500 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Sep 2 08:09:00 2024 +0200 tdf#162715: do not import theme when pasting from clipboard This change makes the two code paths consistent, that are used to paste native ODF data into the Writer document: that uses own transferable in usual copy/paste procedure; and that uses "foreign" clipboard data from another Writer session. The path using own transferable already didn't set document theme. This change passes the "IsInPaste" flag down to the XML reader, to let the latter code path skip the theme when reading ODT. Change-Id: I44d36e4583c58500febd647bb3def6421e585ed6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172688 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> (cherry picked from commit 3cf243ac31098de2d275c722a474a36b330df6e1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172672 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/CppunitTest_sw_core_theme.mk b/sw/CppunitTest_sw_core_theme.mk index 03b42a32e666..5938916bd9df 100644 --- a/sw/CppunitTest_sw_core_theme.mk +++ b/sw/CppunitTest_sw_core_theme.mk @@ -24,6 +24,7 @@ $(eval $(call gb_CppunitTest_use_libraries,sw_core_theme, \ docmodel \ sal \ sfx \ + sot \ subsequenttest \ sw \ swqahelper \ diff --git a/sw/inc/shellio.hxx b/sw/inc/shellio.hxx index 37db03c6d20c..226d9d111713 100644 --- a/sw/inc/shellio.hxx +++ b/sw/inc/shellio.hxx @@ -155,6 +155,7 @@ class SW_DLLPUBLIC SwReader: public SwDocFac OUString msBaseURL; bool mbSkipImages; bool mbSkipInvalidateNumRules = false; + bool mbIsInPaste = false; public: @@ -180,6 +181,8 @@ public: { mbSkipInvalidateNumRules = bSkipInvalidateNumRules; } + void SetInPaste(bool val) { mbIsInPaste = val; } + bool IsInPaste() const { return mbIsInPaste; } protected: void SetBaseURL( const OUString& rURL ) { msBaseURL = rURL; } @@ -232,6 +235,7 @@ protected: bool m_bHasAskTemplateName : 1; bool m_bIgnoreHTMLComments : 1; bool m_bSkipImages : 1; + bool m_bIsInPaste : 1 = false; virtual OUString GetTemplateName(SwDoc& rDoc) const; @@ -269,6 +273,9 @@ public: void SetIgnoreHTMLComments( bool bSet ) { m_bIgnoreHTMLComments = bSet; } + bool IsInPaste() const { return m_bIsInPaste; } + void SetInPaste(bool bSet) { m_bIsInPaste = bSet; } + virtual bool HasGlossaries() const; virtual bool ReadGlossaries( SwTextBlocks&, bool bSaveRelFiles ) const; diff --git a/sw/qa/core/theme/ThemeTest.cxx b/sw/qa/core/theme/ThemeTest.cxx index 4be895dd3f06..fee5a6e56112 100644 --- a/sw/qa/core/theme/ThemeTest.cxx +++ b/sw/qa/core/theme/ThemeTest.cxx @@ -9,17 +9,25 @@ #include <swmodeltestbase.hxx> +#include <com/sun/star/datatransfer/XTransferableSupplier.hpp> +#include <com/sun/star/ucb/SimpleFileAccess.hpp> + #include <memory> #include <docsh.hxx> #include <unotxdoc.hxx> #include <wrtsh.hxx> #include <drawdoc.hxx> #include <IDocumentDrawModelAccess.hxx> + +#include <comphelper/classids.hxx> +#include <comphelper/processfactory.hxx> #include <svx/svdpage.hxx> #include <docmodel/uno/UnoComplexColor.hxx> #include <docmodel/theme/Theme.hxx> #include <ThemeColorChanger.hxx> +#include <sot/exchange.hxx> #include <svx/ColorSets.hxx> +#include <vcl/transfer.hxx> using namespace css; @@ -479,6 +487,187 @@ CPPUNIT_TEST_FIXTURE(SwCoreThemeTest, testThemeChanging) } } +// A simple transferable, that provides only EMBED_SOURCE and OBJECTDESCRIPTOR flavors, taking +// data from an ODT file. This makes the transferable behave just like clipboard content created +// by Writer in a different instance, taking SwTransferable::PasteOLE path. +class TestSimpleFileTransferable : public cppu::WeakImplHelper<css::datatransfer::XTransferable> +{ +public: + TestSimpleFileTransferable(const OUString& fileURL); + css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& flavor) override; + css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors() override; + sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& flavor) override; + +private: + OUString m_fileURL; +}; + +TestSimpleFileTransferable::TestSimpleFileTransferable(const OUString& fileURL) + : m_fileURL(fileURL) +{ +} + +css::uno::Any +TestSimpleFileTransferable::getTransferData(const css::datatransfer::DataFlavor& flavor) +{ + if (flavor.MimeType == SotExchange::GetFormatMimeType(SotClipboardFormatId::EMBED_SOURCE)) + { + auto xSFA(ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext())); + auto xInputStream = xSFA->openFileRead(m_fileURL); + sal_Int32 bytes = xInputStream->available(); + css::uno::Sequence<sal_Int8> data; + CPPUNIT_ASSERT_EQUAL(bytes, xInputStream->readBytes(data, bytes)); + return css::uno::Any(data); + } + if (flavor.MimeType == SotExchange::GetFormatMimeType(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + TransferableObjectDescriptor aDesc; + aDesc.maClassName = SvGlobalName(SO3_SW_CLASSID); + SvMemoryStream aMemStm(1024, 1024); + WriteTransferableObjectDescriptor(aMemStm, aDesc); + css::uno::Sequence<sal_Int8> data(static_cast<const sal_Int8*>(aMemStm.GetData()), + aMemStm.GetSize()); + return css::uno::Any(data); + } + return {}; +} + +css::uno::Sequence<css::datatransfer::DataFlavor> +TestSimpleFileTransferable::getTransferDataFlavors() +{ + css::datatransfer::DataFlavor embed_source; + SotExchange::GetFormatDataFlavor(SotClipboardFormatId::EMBED_SOURCE, embed_source); + css::datatransfer::DataFlavor objectdescriptor; + SotExchange::GetFormatDataFlavor(SotClipboardFormatId::OBJECTDESCRIPTOR, objectdescriptor); + return { embed_source, objectdescriptor }; +} + +sal_Bool +TestSimpleFileTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& flavor) +{ + if (flavor.MimeType == SotExchange::GetFormatMimeType(SotClipboardFormatId::EMBED_SOURCE)) + return true; + if (flavor.MimeType == SotExchange::GetFormatMimeType(SotClipboardFormatId::OBJECTDESCRIPTOR)) + return true; + return false; +} + +CPPUNIT_TEST_FIXTURE(SwCoreThemeTest, testTdf162715_customTransferable) +{ + // Given a document with a custom theme: + createSwDoc("theme_foo.fodt"); + + auto pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + + auto pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + CPPUNIT_ASSERT(pModel); + auto pTheme = pModel->getTheme().get(); + CPPUNIT_ASSERT(pTheme); + CPPUNIT_ASSERT_EQUAL(u"foo"_ustr, pTheme->GetName()); + CPPUNIT_ASSERT_EQUAL(u"colors_foo"_ustr, pTheme->getColorSet()->getName()); + CPPUNIT_ASSERT_EQUAL(Color(0x000080), pTheme->GetColor(model::ThemeColorType::Dark1)); + CPPUNIT_ASSERT_EQUAL(Color(0x008000), pTheme->GetColor(model::ThemeColorType::Dark2)); + + // Select all and check the original text in the document: + auto pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->SelAll(); + CPPUNIT_ASSERT_EQUAL(u"Theme foo"_ustr, pWrtShell->GetSelText()); + + // Create a transferable from another document with another custom theme, + // and insert (paste) its content over the selection: + css::uno::Reference<css::datatransfer::XTransferable> xTransferable( + new TestSimpleFileTransferable(createFileURL(u"theme_bar.odt"))); + css::uno::Reference<css::frame::XModel> xModel(mxComponent, css::uno::UNO_QUERY_THROW); + css::uno::Reference<css::datatransfer::XTransferableSupplier> xTS( + xModel->getCurrentController(), css::uno::UNO_QUERY_THROW); + xTS->insertTransferable(xTransferable); + + // Check that the paste is successful (the text has been replaced): + pWrtShell->SelAll(); + CPPUNIT_ASSERT_EQUAL(u"Theme bar"_ustr, pWrtShell->GetSelText()); + + // The original theme must not be replaced. + pTheme = pModel->getTheme().get(); + CPPUNIT_ASSERT(pTheme); + // Without the fix, this would fail, because the name was "bar": + CPPUNIT_ASSERT_EQUAL(u"foo"_ustr, pTheme->GetName()); + CPPUNIT_ASSERT_EQUAL(u"colors_foo"_ustr, pTheme->getColorSet()->getName()); + CPPUNIT_ASSERT_EQUAL(Color(0x000080), pTheme->GetColor(model::ThemeColorType::Dark1)); + CPPUNIT_ASSERT_EQUAL(Color(0x008000), pTheme->GetColor(model::ThemeColorType::Dark2)); +} + +CPPUNIT_TEST_FIXTURE(SwCoreThemeTest, testTdf162715_ownTransferable) +{ + css::uno::Reference<css::datatransfer::XTransferable> xTransferable; + { + // Given a document with a custom theme: + createSwDoc("theme_bar.odt"); + + auto pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + + auto pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + CPPUNIT_ASSERT(pModel); + auto pTheme = pModel->getTheme().get(); + CPPUNIT_ASSERT(pTheme); + CPPUNIT_ASSERT_EQUAL(u"bar"_ustr, pTheme->GetName()); + CPPUNIT_ASSERT_EQUAL(u"colors_bar"_ustr, pTheme->getColorSet()->getName()); + CPPUNIT_ASSERT_EQUAL(Color(0x606000), pTheme->GetColor(model::ThemeColorType::Dark1)); + CPPUNIT_ASSERT_EQUAL(Color(0x800000), pTheme->GetColor(model::ThemeColorType::Dark2)); + + // Select all and check the original text in the document: + auto pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->SelAll(); + CPPUNIT_ASSERT_EQUAL(u"Theme bar"_ustr, pWrtShell->GetSelText()); + + // Create a normal Writer's transferable out of the selection: + css::uno::Reference<css::frame::XModel> xModel(mxComponent, css::uno::UNO_QUERY_THROW); + css::uno::Reference<css::datatransfer::XTransferableSupplier> xTS( + xModel->getCurrentController(), css::uno::UNO_QUERY_THROW); + xTransferable = xTS->getTransferable(); + } + { + // Open another document with another custom theme: + createSwDoc("theme_foo.fodt"); + + auto pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + + auto pModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel(); + CPPUNIT_ASSERT(pModel); + auto pTheme = pModel->getTheme().get(); + CPPUNIT_ASSERT(pTheme); + CPPUNIT_ASSERT_EQUAL(u"foo"_ustr, pTheme->GetName()); + CPPUNIT_ASSERT_EQUAL(u"colors_foo"_ustr, pTheme->getColorSet()->getName()); + CPPUNIT_ASSERT_EQUAL(Color(0x000080), pTheme->GetColor(model::ThemeColorType::Dark1)); + CPPUNIT_ASSERT_EQUAL(Color(0x008000), pTheme->GetColor(model::ThemeColorType::Dark2)); + + // Select all and check the original text in the second document: + auto pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->SelAll(); + CPPUNIT_ASSERT_EQUAL(u"Theme foo"_ustr, pWrtShell->GetSelText()); + + // Insert (paste) the previously created transferable's content over the selection: + css::uno::Reference<css::frame::XModel> xModel(mxComponent, css::uno::UNO_QUERY_THROW); + css::uno::Reference<css::datatransfer::XTransferableSupplier> xTS( + xModel->getCurrentController(), css::uno::UNO_QUERY_THROW); + xTS->insertTransferable(xTransferable); + + // Check that the paste is successful (the text has been replaced): + pWrtShell->SelAll(); + CPPUNIT_ASSERT_EQUAL(u"Theme bar"_ustr, pWrtShell->GetSelText()); + + // The original theme must not be replaced. + pTheme = pModel->getTheme().get(); + CPPUNIT_ASSERT(pTheme); + CPPUNIT_ASSERT_EQUAL(u"foo"_ustr, pTheme->GetName()); + CPPUNIT_ASSERT_EQUAL(u"colors_foo"_ustr, pTheme->getColorSet()->getName()); + CPPUNIT_ASSERT_EQUAL(Color(0x000080), pTheme->GetColor(model::ThemeColorType::Dark1)); + CPPUNIT_ASSERT_EQUAL(Color(0x008000), pTheme->GetColor(model::ThemeColorType::Dark2)); + } +} + } // end anonymous namnespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/qa/core/theme/data/theme_bar.odt b/sw/qa/core/theme/data/theme_bar.odt new file mode 100644 index 000000000000..30892fcd03aa Binary files /dev/null and b/sw/qa/core/theme/data/theme_bar.odt differ diff --git a/sw/qa/core/theme/data/theme_foo.fodt b/sw/qa/core/theme/data/theme_foo.fodt new file mode 100644 index 000000000000..29837a34d54c --- /dev/null +++ b/sw/qa/core/theme/data/theme_foo.fodt @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:styles> + <loext:theme loext:name="foo"> + <loext:theme-colors loext:name="colors_foo"> + <loext:color loext:name="dark1" loext:color="#000080"/> + <loext:color loext:name="light1" loext:color="#ffffff"/> + <loext:color loext:name="dark2" loext:color="#008000"/> + <loext:color loext:name="light2" loext:color="#ffffff"/> + <loext:color loext:name="accent1" loext:color="#18a303"/> + <loext:color loext:name="accent2" loext:color="#0369a3"/> + <loext:color loext:name="accent3" loext:color="#a33e03"/> + <loext:color loext:name="accent4" loext:color="#8e03a3"/> + <loext:color loext:name="accent5" loext:color="#c99c00"/> + <loext:color loext:name="accent6" loext:color="#c9211e"/> + <loext:color loext:name="hyperlink" loext:color="#0000ee"/> + <loext:color loext:name="followed-hyperlink" loext:color="#551a8b"/> + </loext:theme-colors> + </loext:theme> + </office:styles> + <office:automatic-styles> + <style:style style:name="T1" style:family="text"> + <style:text-properties fo:color="#000080"> + <loext:char-complex-color loext:theme-type="dark1" loext:color-type="theme"/> + </style:text-properties> + </style:style> + <style:style style:name="T2" style:family="text"> + <style:text-properties fo:color="#008000"> + <loext:char-complex-color loext:theme-type="dark2" loext:color-type="theme"/> + </style:text-properties> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <text:p><text:span text:style-name="T1">Theme</text:span> <text:span text:style-name="T2">foo</text:span></text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/source/filter/basflt/shellio.cxx b/sw/source/filter/basflt/shellio.cxx index 0ead0d6d811a..c1a6b5e8b325 100644 --- a/sw/source/filter/basflt/shellio.cxx +++ b/sw/source/filter/basflt/shellio.cxx @@ -97,6 +97,7 @@ ErrCodeMsg SwReader::Read( const Reader& rOptions ) po->m_xStorage = mxStg; po->m_bInsertMode = nullptr != mpCursor; po->m_bSkipImages = mbSkipImages; + po->SetInPaste(IsInPaste()); // if a Medium is selected, get its Stream if( nullptr != (po->m_pMedium = mpMedium ) && @@ -407,6 +408,7 @@ ErrCodeMsg SwReader::Read( const Reader& rOptions ) po->SetBlockMode( false ); po->SetOrganizerMode( false ); po->SetIgnoreHTMLComments( false ); + po->SetInPaste(false); return nError; } diff --git a/sw/source/filter/xml/swxml.cxx b/sw/source/filter/xml/swxml.cxx index 16ae089113b8..1771c67ea178 100644 --- a/sw/source/filter/xml/swxml.cxx +++ b/sw/source/filter/xml/swxml.cxx @@ -591,6 +591,8 @@ ErrCodeMsg XMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("SourceStorage"), 0, cppu::UnoType<embed::XStorage>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { u"IsInPaste"_ustr, 0, cppu::UnoType<bool>::get(), + beans::PropertyAttribute::MAYBEVOID, 0 }, }; uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( @@ -640,6 +642,8 @@ ErrCodeMsg XMLReader::Read( SwDoc &rDoc, const OUString& rBaseURL, SwPaM &rPaM, xInfoSet->setPropertyValue( "SourceStorage", Any( xStorage ) ); + xInfoSet->setPropertyValue(u"IsInPaste"_ustr, Any(IsInPaste())); + // prepare filter arguments, WARNING: the order is important! Sequence<Any> aFilterArgs{ Any(xInfoSet), Any(xStatusIndicator), diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index 80be5fc31189..82c2222134e6 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -2363,6 +2363,7 @@ bool SwTransferable::PasteOLE( TransferableDataHelper& rData, SwWrtShell& rSh, { SwPaM &rPAM = *rSh.GetCursor(); SwReader aReader(xStore, OUString(), rPAM); + aReader.SetInPaste(true); if( ! aReader.Read( *pRead ).IsError() ) bRet = true; else if( bMsg ) diff --git a/xmloff/source/style/xmlstyle.cxx b/xmloff/source/style/xmlstyle.cxx index 168483af1db0..bd3d50be54bb 100644 --- a/xmloff/source/style/xmlstyle.cxx +++ b/xmloff/source/style/xmlstyle.cxx @@ -21,6 +21,7 @@ #include <sal/config.h> +#include <com/sun/star/beans/XPropertySet.hpp> #include <com/sun/star/frame/XModel.hpp> #include <com/sun/star/xml/sax/XAttributeList.hpp> #include <com/sun/star/container/XNameContainer.hpp> @@ -29,6 +30,8 @@ #include <com/sun/star/style/XAutoStyleFamily.hpp> #include <com/sun/star/drawing/XDrawPageSupplier.hpp> #include <PageMasterPropMapper.hxx> + +#include <comphelper/diagnose_ex.hxx> #include <sal/log.hxx> #include <svl/style.hxx> #include <utility> @@ -685,6 +688,26 @@ css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLStylesContext::cr { if (nElement == XML_ELEMENT(LO_EXT, XML_THEME)) { + if (auto xImportInfo = GetImport().getImportInfo()) + { + try + { + if (auto xPropertySetInfo = xImportInfo->getPropertySetInfo()) + { + if (xPropertySetInfo->hasPropertyByName(u"IsInPaste"_ustr)) + { + css::uno::Any value = xImportInfo->getPropertyValue(u"IsInPaste"_ustr); + if (bool b; (value >>= b) && b) + return nullptr; // do not import themes in paste mode + } + } + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("xmloff"); + } + } + uno::Reference<uno::XInterface> xObject(GetImport().GetModel(), uno::UNO_QUERY); uno::Reference<drawing::XDrawPageSupplier> const xDrawPageSupplier(GetImport().GetModel(), uno::UNO_QUERY); if (xDrawPageSupplier.is())
