Kaydet (Commit) 1428ec6f authored tarafından Jacobo Aragunde Pérez's avatar Jacobo Aragunde Pérez

sw: Preserve embedded spreadsheets in docx

Embedded spreadsheets consist on two files:

* The spreadsheet found in word/embeddings/ directory.
* A preview image found in word/media/ directory.

This patch saves these two files and writes the proper XML to the
document. It looks like this:

    <w:object>
      <v:shape id="ole_rId2"
      style="width:362.55pt;height:145.7pt" o:ole="">
        <v:imagedata r:id="rId3" />
      </v:shape>
      <o:OLEObject Type="Embed" ProgID="Excel.Sheet.12"
      ShapeID="ole_rId2" DrawAspect="Content"
      ObjectID="_227653443" r:id="rId2" />
    </w:object>

Some simplifications were done in comparison with the XML generated
by Word; the lines above seem to be the minimum working code.

Similarly to drawings, charts, etc. our code postpones the actual
process of the OLE objects until the rPr tag is closed.

TODO:
* Unit tests.
* Add information about xlsx extension to [Content_Types].xml
  * Without that, Word doesn't detect the OLE as an spreadsheet.

Change-Id: Ia0c797b72cd6e99ca9ad7fa11897b62ab3867a5e
üst ad7fdc4e
......@@ -1071,6 +1071,9 @@ void DocxAttributeOutput::StartRunProperties()
assert(!m_postponedDMLDrawing);
m_postponedDMLDrawing = new std::list< PostponedDrawing >;
assert( !m_postponedOLE );
m_postponedOLE = new std::list< PostponedOLE >;
}
void DocxAttributeOutput::InitCollectedRunProperties()
......@@ -1326,6 +1329,8 @@ void DocxAttributeOutput::EndRunProperties( const SwRedlineData* pRedlineData )
WritePostponedVMLDrawing();
WritePostponedDMLDrawing();
WritePostponedOLE();
// merge the properties _before_ the run text (strictly speaking, just
// after the start of the run)
m_pSerializer->mergeTopMarks( sax_fastparser::MERGE_MARKS_PREPEND );
......@@ -3306,6 +3311,8 @@ void DocxAttributeOutput::WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rOL
return;
if( WriteOLEMath( pSdrObj, rOLENode, rSize ))
return;
if( PostponeOLE( pSdrObj, rOLENode, rSize, pFlyFrmFmt ))
return;
// Then we fall back to just export the object as a graphic.
if( m_postponedGraphic == NULL )
FlyFrameGraphic( 0, rSize, pFlyFrmFmt, &rOLENode );
......@@ -3467,6 +3474,83 @@ void DocxAttributeOutput::WritePostponedFormControl(const SdrObject* pObject)
}
}
bool DocxAttributeOutput::PostponeOLE( const SdrObject*, SwOLENode& rNode, const Size& rSize, const SwFlyFrmFmt* pFlyFrmFmt )
{
if( m_postponedVMLDrawing == NULL )
return false;
m_postponedOLE->push_back( PostponedOLE( &rNode, rSize, pFlyFrmFmt ) );
return true;
}
/*
* Write w:object hierarchy for embedded objects after end element of w:rPr tag.
*/
void DocxAttributeOutput::WritePostponedOLE()
{
if( m_postponedOLE == NULL )
return;
SAL_INFO( "sw.ww8", OSL_THIS_FUNC );
for( std::list< PostponedOLE >::iterator it = m_postponedOLE->begin();
it != m_postponedOLE->end();
++it )
{
// write embedded file
OString sId = m_rExport.WriteOLENode( *it->object );
if( sId.isEmpty() )
{
// the embedded file could not be saved
// fallback: save as an image
FlyFrameGraphic( 0, it->size, it->frame, it->object );
continue;
}
// write preview image
const Graphic* pGraphic = const_cast< SwOLENode* >( it->object )->GetGraphic();
OUString sImageId = m_rDrawingML.WriteImage( *pGraphic );
m_pSerializer->startElementNS( XML_w, XML_object, FSEND );
OStringBuffer sShapeStyle, sShapeId;
sShapeStyle.append( "width:" ).append( double( it->size.Width() ) / 20 )
.append( "pt;height:" ).append( double( it->size.Height() ) / 20 )
.append( "pt" ); //from VMLExport::AddRectangleDimensions(), it does: value/20
sShapeId.append( "ole_" ).append( sId );
// shape definition
m_pSerializer->startElementNS( XML_v, XML_shape,
XML_id, sShapeId.getStr(),
XML_style, sShapeStyle.getStr(),
FSNS( XML_o, XML_ole ), "", //compulsory, even if it's empty
FSEND );
// shape filled with the preview image
m_pSerializer->singleElementNS( XML_v, XML_imagedata,
FSNS( XML_r, XML_id ), OUStringToOString( sImageId, RTL_TEXTENCODING_UTF8 ).getStr(),
FSEND );
m_pSerializer->endElementNS( XML_v, XML_shape );
// 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_ShapeID, sShapeId.getStr(),
XML_DrawAspect, "Content",
XML_ObjectID, "_" + OString::number( rand() ),
FSNS( XML_r, XML_id ), sId.getStr(),
FSEND );
m_pSerializer->endElementNS( XML_w, XML_object );
}
// clear list of postponed objects
delete m_postponedOLE;
m_postponedOLE = NULL;
}
/*
* Write w:pict hierarchy end element of w:rPr tag.
*/
......@@ -6520,6 +6604,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, FSHelperPtr pSeri
m_postponedDiagram( NULL ),
m_postponedVMLDrawing(NULL),
m_postponedDMLDrawing(NULL),
m_postponedOLE( NULL ),
m_postponedMath( NULL ),
m_postponedChart( NULL ),
pendingPlaceholder( NULL ),
......
......@@ -393,6 +393,7 @@ private:
void WriteOLE2Obj( const SdrObject* pSdrObj, SwOLENode& rNode, const Size& rSize, const SwFlyFrmFmt* pFlyFrmFmt);
bool WriteOLEChart( const SdrObject* pSdrObj, const Size& rSize );
bool WriteOLEMath( const SdrObject* pSdrObj, const SwOLENode& rNode, const Size& rSize );
bool PostponeOLE( const SdrObject* pSdrObj, SwOLENode& rNode, const Size& rSize, const SwFlyFrmFmt* pFlyFrmFmt );
/// checks whether the current component is a diagram
bool IsDiagram (const SdrObject* sdrObject);
......@@ -668,6 +669,7 @@ private:
void WritePostponedFormControl(const SdrObject* pObject);
void WritePostponedDiagram();
void WritePostponedChart();
void WritePostponedOLE();
void WritePostponedVMLDrawing();
void WritePostponedDMLDrawing();
......@@ -786,6 +788,15 @@ private:
std::list< PostponedDrawing >* m_postponedVMLDrawing;
std::list< PostponedDrawing >* m_postponedDMLDrawing;
struct PostponedOLE
{
PostponedOLE( SwOLENode* rObject, const Size rSize, const SwFlyFrmFmt* rFrame ) : object( rObject ), size( rSize ), frame( rFrame ) {};
SwOLENode* object;
const Size size;
const SwFlyFrmFmt* frame;
};
std::list< PostponedOLE >* m_postponedOLE;
const SwOLENode* m_postponedMath;
const SdrObject* m_postponedChart;
Size m_postponedChartSize;
......
......@@ -66,6 +66,7 @@
#include "ww8par.hxx"
#include "ww8scan.hxx"
#include <oox/token/properties.hxx>
#include <comphelper/embeddedobjectcontainer.hxx>
#include <comphelper/string.hxx>
#include <rtl/ustrbuf.hxx>
#include <vcl/font.hxx>
......@@ -357,6 +358,45 @@ OString DocxExport::OutputChart( uno::Reference< frame::XModel >& xModel, sal_In
return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
}
OString DocxExport::WriteOLENode( const SwOLENode& rNode )
{
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 );
OUString sFileName = "embeddings/Microsoft_Excel_Worksheet" + OUString::number( ++m_nOLEObjects ) + ".xlsx";
uno::Reference< io::XOutputStream > xOutStream = GetFilter().openFragmentStream( OUStringBuffer()
.appendAscii( "word/" )
.append( sFileName )
.makeStringAndClear(),
sMediaType );
if( lcl_CopyStream( xInStream, xOutStream ) )
sId = m_pFilter->addRelation( m_pDocumentFS->getOutputStream(),
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/package",
sFileName, false );
return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
}
// function copied from embeddedobj/source/msole/oleembed.cxx
bool DocxExport::lcl_CopyStream( uno::Reference<io::XInputStream> xIn, uno::Reference<io::XOutputStream> xOut )
{
const sal_Int32 nChunkSize = 4096;
uno::Sequence< sal_Int8 > aData(nChunkSize);
sal_Int32 nTotalRead = 0;
sal_Int32 nRead = 0;
do
{
nRead = xIn->readBytes(aData, nChunkSize);
nTotalRead += nRead;
xOut->writeBytes(aData);
} while (nRead == nChunkSize);
return nTotalRead != 0;
}
void DocxExport::OutputDML(uno::Reference<drawing::XShape>& xShape)
{
uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY_THROW);
......@@ -1319,6 +1359,7 @@ DocxExport::DocxExport( DocxExportFilter *pFilter, SwDoc *pDocument, SwPaM *pCur
m_pSections( NULL ),
m_nHeaders( 0 ),
m_nFooters( 0 ),
m_nOLEObjects( 0 ),
m_nHeadersFootersInSection(0),
m_pVMLExport( NULL ),
m_pSdrExport( NULL )
......
......@@ -29,6 +29,7 @@
#include <cstdio>
#include <vector>
#include <boost/optional.hpp>
#include <ndole.hxx>
class DocxAttributeOutput;
class DocxExportFilter;
......@@ -84,6 +85,9 @@ class DocxExport : public MSWordExportBase
/// Footer counter.
sal_Int32 m_nFooters;
/// OLE objects counter.
sal_Int32 m_nOLEObjects;
///Footer and Header counter in Section properties
sal_Int32 m_nHeadersFootersInSection;
......@@ -159,6 +163,9 @@ 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 );
bool lcl_CopyStream( css::uno::Reference< css::io::XInputStream> xIn, css::uno::Reference< css::io::XOutputStream > xOut );
/// Writes the shape using drawingML syntax.
void OutputDML( com::sun::star::uno::Reference< com::sun::star::drawing::XShape >& xShape );
......
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