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 <o:OLEObject> 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
<o:OLEObject> 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
This commit is contained in:
Jacobo Aragunde Pérez 2014-02-26 18:29:44 +01:00
parent 72d9fa114a
commit 1c993627d8
6 changed files with 101 additions and 13 deletions

View File

@ -2579,7 +2579,7 @@ DECLARE_OOXMLEXPORT_TEST(testEmbeddedXlsx, "embedded-xlsx.docx")
int nImageFiles = 0;
for (int i=0; i<names.getLength(); i++)
{
if(names[i].startsWith("word/embeddings/Microsoft_Excel_Worksheet"))
if(names[i].startsWith("word/embeddings/oleObject"))
nSheetFiles++;
if(names[i].startsWith("word/media/image"))
nImageFiles++;

View File

@ -3568,12 +3568,53 @@ void DocxAttributeOutput::WritePostponedOLE()
SAL_INFO( "sw.ww8", OSL_THIS_FUNC );
// get interoperability information about embedded objects
uno::Reference< beans::XPropertySet > 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() ),

View File

@ -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 <embed::XEmbeddedObject> xObj( const_cast<SwOLENode&>(rNode).GetOLEObj().GetOleRef() );
OUString sId, sMediaType;
comphelper::EmbeddedObjectContainer* aContainer = const_cast<SwOLENode&>(rNode).GetOLEObj().GetObject().GetContainer();
uno::Reference< io::XInputStream > xInStream = aContainer->GetObjectStream( xObj, &sMediaType );
uno::Reference <embed::XEmbeddedObject> 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 );
}

View File

@ -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.

View File

@ -20,6 +20,7 @@
#include <PropertyMap.hxx>
#include "GraphicHelpers.hxx"
#include <editeng/unoprnms.hxx>
#include <ooxml/resourceids.hxx>
#include <rtl/ustring.hxx>
#include <com/sun/star/beans/PropertyValue.hpp>
@ -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() );

View File

@ -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();