Kaydet (Commit) 8a59b30b authored tarafından Miklos Vajna's avatar Miklos Vajna

Related: tdf#108269 DOCM filter: preserve VBA stream

This means 2 new streams when roundtripping DOCM files that actually
have macros: word/vbaProject.bin and word/vbaData.xml (+ the relation
pointing to the second from the first).

Change-Id: Iba24eea4c5bca8f743a53027c71ed2aae48f1934
Reviewed-on: https://gerrit.libreoffice.org/38360Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarMiklos Vajna <vmiklos@collabora.co.uk>
üst ecad6d74
......@@ -93,6 +93,18 @@ DECLARE_SW_ROUNDTRIP_TEST(testBadDocm, "bad.docm", nullptr, DocmTest)
CPPUNIT_ASSERT_EQUAL(OUString("MS Word 2007 XML VBA"), pTextDoc->GetDocShell()->GetMedium()->GetFilter()->GetName());
}
DECLARE_SW_ROUNDTRIP_TEST(testTdf108269, "tdf108269.docm", nullptr, DocmTest)
{
if (!mbExported)
return;
uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), maTempFile.GetURL());
// This failed: VBA streams were not roundtripped via the doc-level
// grab-bag.
CPPUNIT_ASSERT(xNameAccess->hasByName("word/vbaProject.bin"));
CPPUNIT_ASSERT(xNameAccess->hasByName("word/vbaData.xml"));
}
DECLARE_OOXMLEXPORT_TEST(testTdf92045, "tdf92045.docx")
{
// This was true, <w:effect w:val="none"/> resulted in setting the blinking font effect.
......
......@@ -77,6 +77,7 @@
#include <comphelper/string.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/font.hxx>
#include <unotools/ucbstreamhelper.hxx>
using namespace sax_fastparser;
using namespace ::comphelper;
......@@ -464,6 +465,8 @@ void DocxExport::ExportDocument_Impl()
WriteEmbeddings();
WriteVBA();
m_aLinkedTextboxesHelper.clear(); //final cleanup
delete m_pStyles;
m_pStyles = nullptr;
......@@ -1251,6 +1254,78 @@ void DocxExport::WriteActiveX()
}
}
void DocxExport::WriteVBA()
{
uno::Reference<beans::XPropertySet> xPropertySet(m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
if (!xPropertySet.is())
return;
uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
if (!xPropertySetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
return;
uno::Sequence<beans::PropertyValue> aGrabBag;
xPropertySet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= aGrabBag;
uno::Sequence<beans::PropertyValue> aVBA;
for (const auto& rProperty : aGrabBag)
{
if (rProperty.Name == "OOXVBA")
rProperty.Value >>= aVBA;
}
if (!aVBA.hasElements())
return;
uno::Reference<io::XOutputStream> xProjectStream;
for (const auto& rProperty : aVBA)
{
if (rProperty.Name == "ProjectStream")
{
// First check for the project stream, this sets xProjectStream.
uno::Reference<io::XStream> xInputStream;
rProperty.Value >>= xInputStream;
if (!xInputStream.is())
return;
std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xInputStream));
xProjectStream = GetFilter().openFragmentStream("word/vbaProject.bin", "application/vnd.ms-office.vbaProject");
uno::Reference<io::XStream> xOutputStream(xProjectStream, uno::UNO_QUERY);
if (!xOutputStream.is())
return;
std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream));
// Write the stream.
pOut->WriteStream(*pIn);
// Write the relationship.
m_pFilter->addRelation(m_pDocumentFS->getOutputStream(), "http://schemas.microsoft.com/office/2006/relationships/vbaProject", "vbaProject.bin");
}
else if (rProperty.Name == "DataStream")
{
// Then the data stream, which wants to work with an already set
// xProjectStream.
uno::Reference<io::XStream> xInputStream;
rProperty.Value >>= xInputStream;
if (!xInputStream.is())
return;
std::unique_ptr<SvStream> pIn(utl::UcbStreamHelper::CreateStream(xInputStream));
uno::Reference<io::XStream> xOutputStream(GetFilter().openFragmentStream("word/vbaData.xml", "application/vnd.ms-word.vbaData+xml"), uno::UNO_QUERY);
if (!xOutputStream.is())
return;
std::unique_ptr<SvStream> pOut(utl::UcbStreamHelper::CreateStream(xOutputStream));
// Write the stream.
pOut->WriteStream(*pIn);
// Write the relationship.
if (!xProjectStream.is())
return;
m_pFilter->addRelation(xProjectStream, "http://schemas.microsoft.com/office/2006/relationships/wordVbaData", "vbaData.xml");
}
}
}
void DocxExport::WriteEmbeddings()
{
uno::Reference< beans::XPropertySet > xPropSet( m_pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW );
......
......@@ -244,6 +244,9 @@ private:
/// Write word/embeddings/Worksheet[n].xlsx
void WriteEmbeddings();
/// Writes word/vbaProject.bin.
void WriteVBA();
/// return true if Page Layout is set as Mirrored
bool isMirroredMargin();
......
......@@ -229,6 +229,7 @@ public:
virtual css::uno::Sequence<css::uno::Reference<css::xml::dom::XDocument> > getActiveXDomList( ) = 0;
virtual css::uno::Sequence<css::uno::Reference<css::io::XInputStream> > getActiveXBinList() = 0;
virtual css::uno::Sequence<css::beans::PropertyValue > getEmbeddingsList() = 0;
virtual css::uno::Sequence<css::beans::PropertyValue > getVBA() = 0;
};
......
......@@ -236,6 +236,9 @@ sal_Bool WriterFilter::filter(const uno::Sequence< beans::PropertyValue >& aDesc
// Adding the saved embedding document to document's grab bag
aGrabBagProperties["OOXEmbeddings"] <<= pDocument->getEmbeddingsList();
if (pDocument->getVBA().hasElements())
aGrabBagProperties["OOXVBA"] <<= pDocument->getVBA();
putPropertiesToDocumentGrabBag(aGrabBagProperties);
writerfilter::ooxml::OOXMLStream::Pointer_t pVBAProjectStream(writerfilter::ooxml::OOXMLDocumentFactory::createStream(pDocStream, writerfilter::ooxml::OOXMLStream::VBAPROJECT));
......
......@@ -39,6 +39,7 @@
#include <svx/dialogs.hrc>
#include <comphelper/sequence.hxx>
#include <unotools/mediadescriptor.hxx>
#include <comphelper/propertysequence.hxx>
#include <iostream>
#include "sfx2/objsh.hxx"
......@@ -496,6 +497,9 @@ void OOXMLDocumentImpl::resolve(Stream & rStream)
resolveActiveXStream(rStream);
if (!mbIsSubstream)
preserveVBA();
resolveFastSubStream(rStream, OOXMLStream::FONTTABLE);
resolveFastSubStream(rStream, OOXMLStream::STYLES);
resolveFastSubStream(rStream, OOXMLStream::NUMBERING);
......@@ -808,6 +812,72 @@ void OOXMLDocumentImpl::resolveEmbeddingsStream(const OOXMLStream::Pointer_t& pS
mxEmbeddingsList = comphelper::containerToSequence(aEmbeddings);
}
namespace
{
/// Returns the target string for rType in xRelationshipAccess.
OUString getTypeTarget(const uno::Reference<embed::XRelationshipAccess>& xRelationshipAccess, const OUString& rType)
{
uno::Sequence< uno::Sequence<beans::StringPair> > aRelations = xRelationshipAccess->getAllRelationships();
for (const auto& rRelation : aRelations)
{
OUString aType;
OUString aTarget;
for (const auto& rPair : rRelation)
{
if (rPair.First == "Type")
aType = rPair.Second;
else if (rPair.First == "Target")
aTarget = rPair.Second;
}
if (aType == rType)
return aTarget;
}
return OUString();
}
}
void OOXMLDocumentImpl::preserveVBA()
{
auto pOOXMLStream = dynamic_cast<OOXMLStreamImpl*>(mpStream.get());
if (!pOOXMLStream)
return;
uno::Reference<embed::XRelationshipAccess> xRelationshipAccess(pOOXMLStream->accessDocumentStream(), uno::UNO_QUERY);
if (!xRelationshipAccess.is())
return;
OUString aVBAStreamName = getTypeTarget(xRelationshipAccess, "http://schemas.microsoft.com/office/2006/relationships/vbaProject");
if (aVBAStreamName.isEmpty())
return;
uno::Reference<embed::XHierarchicalStorageAccess> xStorage(pOOXMLStream->getStorage(), uno::UNO_QUERY);
if (!xStorage.is())
return;
OUString aPath = pOOXMLStream->getPath();
uno::Reference<io::XStream> xStream(xStorage->openStreamElementByHierarchicalName(aPath + aVBAStreamName, embed::ElementModes::SEEKABLEREAD), uno::UNO_QUERY);
if (!xStream.is())
return;
xRelationshipAccess.set(xStream, uno::UNO_QUERY);
uno::Reference<io::XStream> xDataStream;
if (xRelationshipAccess.is())
{
// Check if there is a vbaData.xml for the vbaProject.bin.
OUString aVBAData = getTypeTarget(xRelationshipAccess, "http://schemas.microsoft.com/office/2006/relationships/wordVbaData");
if (!aVBAData.isEmpty())
xDataStream.set(xStorage->openStreamElementByHierarchicalName(aPath + aVBAData, embed::ElementModes::SEEKABLEREAD), uno::UNO_QUERY);
}
maVBA = comphelper::InitPropertySequence(
{
{"ProjectStream", uno::makeAny(xStream)},
{"DataStream", uno::makeAny(xDataStream)}
});
}
void OOXMLDocumentImpl::resolveActiveXStream(Stream & rStream)
{
// Resolving all ActiveX[n].xml files from ActiveX folder.
......@@ -947,6 +1017,11 @@ uno::Sequence<beans::PropertyValue > OOXMLDocumentImpl::getEmbeddingsList( )
return mxEmbeddingsList;
}
uno::Sequence<beans::PropertyValue> OOXMLDocumentImpl::getVBA()
{
return maVBA;
}
OOXMLDocument *
OOXMLDocumentFactory::createDocument
(const OOXMLStream::Pointer_t& pStream,
......
......@@ -53,6 +53,8 @@ class OOXMLDocumentImpl : public OOXMLDocument
css::uno::Reference<css::io::XInputStream> mxActiveXBin;
css::uno::Reference<css::io::XInputStream> mxEmbeddings;
css::uno::Sequence < css::beans::PropertyValue > mxEmbeddingsList;
/// List of VBA-related streams.
css::uno::Sequence<css::beans::PropertyValue> maVBA;
bool mbIsSubstream;
bool mbSkipImages;
/// How many paragraphs equal to 1 percent?
......@@ -91,6 +93,7 @@ protected:
void resolveActiveXStream(Stream & rStream);
void resolveGlossaryStream(Stream & rStream);
void resolveEmbeddingsStream(const OOXMLStream::Pointer_t& pStream);
void preserveVBA();
public:
OOXMLDocumentImpl(OOXMLStream::Pointer_t const & pStream, const css::uno::Reference<css::task::XStatusIndicator>& xStatusIndicator, bool bSkipImages, const css::uno::Sequence<css::beans::PropertyValue>& rDescriptor);
virtual ~OOXMLDocumentImpl() override;
......@@ -136,6 +139,7 @@ public:
virtual css::uno::Reference<css::xml::dom::XDocument> getGlossaryDocDom() override;
virtual css::uno::Sequence<css::uno::Sequence< css::uno::Any> > getGlossaryDomList() override;
virtual css::uno::Sequence<css::beans::PropertyValue > getEmbeddingsList() override;
virtual css::uno::Sequence<css::beans::PropertyValue> getVBA() override;
void incrementProgress();
bool IsSkipImages() { return mbSkipImages; };
......
......@@ -80,6 +80,9 @@ public:
// Giving access to mxDocumentStream. It is needed by resolving custom xml to get list of customxml's used in document.
const css::uno::Reference<css::io::XStream>& accessDocumentStream() { return mxDocumentStream;}
const css::uno::Reference<css::embed::XStorage>& getStorage() { return mxStorage; }
const OUString& getPath() { return msPath; }
};
}}
#endif // INCLUDED_WRITERFILTER_SOURCE_OOXML_OOXMLSTREAMIMPL_HXX
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment