From 1c993627d893c62ab8f01355895206374bf4eb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacobo=20Aragunde=20P=C3=A9rez?= Date: Wed, 26 Feb 2014 18:29:44 +0100 Subject: [PATCH] sw: write other types of embeddings properly to docx Improves patch 1428ec6f4e2bfe0d8654a9ccc713e274e08c6423 When embedding an object into a docx, several things interact: * The properties in the tag in document.xml * The ContentType for the file defined in [Content_Types].xml * The Type of the Relationship defined in document.xml.rels You need the right combination of those three elements for Word to properly recognize the embeddings in exported documents. To know which values must be written, I store some interoperability information in the import phase in the document grab bag. The relevant information is the value of the ProgID attribute in the tag. I have defined three cases depending on the value of ProgID, but more could be needed in the future: * Embedded xlsx sheet. * Embedded pptx presentation. * Generic OLE, this should work with embedded odt/ods in combination with the ProgID attribute stored in the import phase. Change-Id: I26336cb3fe47bd33e1cef11dd1c7edcf390f2e56 --- sw/qa/extras/ooxmlexport/ooxmlexport.cxx | 2 +- sw/source/filter/ww8/docxattributeoutput.cxx | 45 +++++++++++++++++++- sw/source/filter/ww8/docxexport.cxx | 16 +++---- sw/source/filter/ww8/docxexport.hxx | 2 +- writerfilter/source/dmapper/OLEHandler.cxx | 45 ++++++++++++++++++++ writerfilter/source/dmapper/OLEHandler.hxx | 4 ++ 6 files changed, 101 insertions(+), 13 deletions(-) diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx index 675a012617ed..12e599211730 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport.cxx @@ -2579,7 +2579,7 @@ DECLARE_OOXMLEXPORT_TEST(testEmbeddedXlsx, "embedded-xlsx.docx") int nImageFiles = 0; for (int i=0; i xPropSet( m_rExport.pDoc->GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW ); + OUString pName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + uno::Sequence< beans::PropertyValue > aGrabBag, aObjectsInteropList; + xPropSet->getPropertyValue( pName ) >>= aGrabBag; + for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i ) + if ( aGrabBag[i].Name == "EmbeddedObjects" ) + { + aGrabBag[i].Value >>= aObjectsInteropList; + break; + } + for( std::list< PostponedOLE >::iterator it = m_postponedOLE->begin(); it != m_postponedOLE->end(); ++it ) { + SwOLEObj& aObject = it->object->GetOLEObj(); + uno::Reference < embed::XEmbeddedObject > xObj( aObject.GetOleRef() ); + comphelper::EmbeddedObjectContainer* aContainer = aObject.GetObject().GetContainer(); + OUString sObjectName = aContainer->GetEmbeddedObjectName( xObj ); + + // set some attributes according to the type of the embedded object + OUString sProgID, sMediaType, sRelationType; + for( sal_Int32 i=0; i < aObjectsInteropList.getLength(); ++i ) + if ( aObjectsInteropList[i].Name == sObjectName ) + { + aObjectsInteropList[i].Value >>= sProgID; + break; + } + if( sProgID.startsWith("Excel.Sheet") ) + { + sMediaType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + sRelationType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package"; + } + else if( sProgID.startsWith("PowerPoint.Show") ) + { + sMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; + sRelationType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package"; + } + else + { + sMediaType = "application/vnd.openxmlformats-officedocument.oleObject"; + sRelationType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/oleObject"; + } + // write embedded file - OString sId = m_rExport.WriteOLENode( *it->object ); + OString sId = m_rExport.WriteOLEObject( aObject, sMediaType, sRelationType ); if( sId.isEmpty() ) { @@ -3612,7 +3653,7 @@ void DocxAttributeOutput::WritePostponedOLE() // OLE object definition m_pSerializer->singleElementNS( XML_o, XML_OLEObject, XML_Type, "Embed", - XML_ProgID,"Excel.Sheet.12", //TODO: should be auto-detected somehow + XML_ProgID, OUStringToOString( sProgID, RTL_TEXTENCODING_UTF8 ).getStr(), XML_ShapeID, sShapeId.getStr(), XML_DrawAspect, "Content", XML_ObjectID, "_" + OString::number( rand() ), diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 41e70834de67..79077f5c554a 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -358,25 +358,23 @@ OString DocxExport::OutputChart( uno::Reference< frame::XModel >& xModel, sal_In return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 ); } -OString DocxExport::WriteOLENode( const SwOLENode& rNode ) +OString DocxExport::WriteOLEObject( SwOLEObj& rObject, OUString sMediaType, OUString sRelationType ) { - uno::Reference xObj( const_cast(rNode).GetOLEObj().GetOleRef() ); - OUString sId, sMediaType; - comphelper::EmbeddedObjectContainer* aContainer = const_cast(rNode).GetOLEObj().GetObject().GetContainer(); - uno::Reference< io::XInputStream > xInStream = aContainer->GetObjectStream( xObj, &sMediaType ); + uno::Reference xObj( rObject.GetOleRef() ); + comphelper::EmbeddedObjectContainer* aContainer = rObject.GetObject().GetContainer(); + uno::Reference< io::XInputStream > xInStream = aContainer->GetObjectStream( xObj, NULL ); - OUString sFileName = "embeddings/Microsoft_Excel_Worksheet" + OUString::number( ++m_nOLEObjects ) + ".xlsx"; + OUString sFileName = "embeddings/oleObject" + OUString::number( ++m_nOLEObjects ) + ".bin"; uno::Reference< io::XOutputStream > xOutStream = GetFilter().openFragmentStream( OUStringBuffer() .appendAscii( "word/" ) .append( sFileName ) .makeStringAndClear(), sMediaType ); - + OUString sId; if( lcl_CopyStream( xInStream, xOutStream ) ) sId = m_pFilter->addRelation( m_pDocumentFS->getOutputStream(), - "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package", - sFileName, false ); + sRelationType, sFileName, false ); return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 ); } diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx index 4ce76e31d7d1..01679d122a96 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -165,7 +165,7 @@ public: /// Returns the relationd id OString OutputChart( com::sun::star::uno::Reference< com::sun::star::frame::XModel >& xModel, sal_Int32 nCount, ::sax_fastparser::FSHelperPtr m_pSerializer ); - OString WriteOLENode( const SwOLENode& rNode ); + OString WriteOLEObject( SwOLEObj& rObject, OUString sMediaType, OUString sRelationType ); bool lcl_CopyStream( css::uno::Reference< css::io::XInputStream> xIn, css::uno::Reference< css::io::XOutputStream > xOut ); /// Writes the shape using drawingML syntax. diff --git a/writerfilter/source/dmapper/OLEHandler.cxx b/writerfilter/source/dmapper/OLEHandler.cxx index f9487e2aa3a4..fb49aa405df9 100644 --- a/writerfilter/source/dmapper/OLEHandler.cxx +++ b/writerfilter/source/dmapper/OLEHandler.cxx @@ -20,6 +20,7 @@ #include #include "GraphicHelpers.hxx" +#include #include #include #include @@ -170,6 +171,48 @@ void OLEHandler::lcl_sprm(Sprm & rSprm) } +void OLEHandler::saveInteropProperties( uno::Reference< text::XTextDocument > xTextDocument, OUString sObjectName ) +{ + const OUString sGrabBagPropName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG; + const OUString sEmbeddingsPropName = "EmbeddedObjects"; + + // get interop grab bag from document + uno::Reference< beans::XPropertySet > xDocProps( xTextDocument, uno::UNO_QUERY ); + uno::Sequence< beans::PropertyValue > aGrabBag; + xDocProps->getPropertyValue( sGrabBagPropName ) >>= aGrabBag; + + // get EmbeddedObjects property inside grab bag + sal_Int32 i = 0; + sal_Int32 nBagLength = aGrabBag.getLength(); + uno::Sequence< beans::PropertyValue > objectsList; + for( ; i < nBagLength; ++i ) + if ( aGrabBag[i].Name == sEmbeddingsPropName ) + { + aGrabBag[i].Value >>= objectsList; + break; + } + + // save ProgID of current object + sal_Int32 length = objectsList.getLength(); + objectsList.realloc( length + 1 ); + objectsList[length].Name = sObjectName; + objectsList[length].Value = uno::Any( m_sProgId ); + + // put objects list back into the grab bag + if( i == nBagLength ) + { + aGrabBag.realloc( nBagLength + 1 ); + aGrabBag[nBagLength].Name = sEmbeddingsPropName; + aGrabBag[nBagLength].Value = uno::Any( objectsList ); + } + else + aGrabBag[i].Value = uno::Any( objectsList ); + + // put grab bag back into the document + xDocProps->setPropertyValue( sGrabBagPropName, uno::Any( aGrabBag ) ); +} + + OUString OLEHandler::copyOLEOStream( uno::Reference< text::XTextDocument > xTextDocument ) { OUString sRet; @@ -202,6 +245,8 @@ OUString OLEHandler::copyOLEOStream( uno::Reference< text::XTextDocument > xText } } + saveInteropProperties( xTextDocument, aURL ); + static const OUString sProtocol("vnd.sun.star.EmbeddedObject:"); OUString aPersistName( xEmbeddedResolver->resolveEmbeddedObjectURL( aURL ) ); sRet = aPersistName.copy( sProtocol.getLength() ); diff --git a/writerfilter/source/dmapper/OLEHandler.hxx b/writerfilter/source/dmapper/OLEHandler.hxx index 1416a708f928..7a76dd076605 100644 --- a/writerfilter/source/dmapper/OLEHandler.hxx +++ b/writerfilter/source/dmapper/OLEHandler.hxx @@ -69,6 +69,10 @@ class OLEHandler : public LoggedProperties virtual void lcl_attribute(Id Name, Value & val); virtual void lcl_sprm(Sprm & sprm); + // Interoperability + virtual void saveInteropProperties( ::com::sun::star::uno::Reference< ::com::sun::star::text::XTextDocument > xTextDocument, + OUString sObjectName ); + public: OLEHandler(); virtual ~OLEHandler();