1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <com/sun/star/frame/Desktop.hpp>
21 #include <com/sun/star/frame/XStorable.hpp>
22 #include <com/sun/star/document/XFilter.hpp>
23 #include <com/sun/star/document/XExporter.hpp>
24 #include <com/sun/star/lang/XInitialization.hpp>
25 #include <com/sun/star/lang/XServiceInfo.hpp>
26 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
27 #include <com/sun/star/drawing/XDrawView.hpp>
28 #include <com/sun/star/container/XIndexAccess.hpp>
29 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
30 #include <com/sun/star/io/IOException.hpp>
31 #include <com/sun/star/io/XOutputStream.hpp>
33 #include <com/sun/star/drawing/XDrawPage.hpp>
34 #include <com/sun/star/drawing/XShapes.hpp>
35 #include <com/sun/star/frame/XController.hpp>
36 #include <com/sun/star/view/XSelectionSupplier.hpp>
38 #include <cppuhelper/implbase.hxx>
39 #include <cppuhelper/supportsservice.hxx>
40 #include <comphelper/processfactory.hxx>
41 #include <osl/file.hxx>
43 #include "swfexporter.hxx"
48 using namespace ::com::sun::star::uno
;
49 using namespace ::com::sun::star::frame
;
50 using namespace ::com::sun::star::lang
;
51 using namespace ::com::sun::star::drawing
;
52 using namespace ::com::sun::star::task
;
53 using namespace ::com::sun::star::view
;
55 using ::com::sun::star::lang::XComponent
;
56 using ::com::sun::star::beans::PropertyValue
;
57 using ::com::sun::star::io::XOutputStream
;
58 using ::com::sun::star::container::XIndexAccess
;
62 class OslOutputStreamWrapper
: public ::cppu::WeakImplHelper
<css::io::XOutputStream
>
67 explicit OslOutputStreamWrapper(const OUString
& rFileName
) : maFile(rFileName
)
69 osl_removeFile(rFileName
.pData
);
70 (void)maFile
.open(osl_File_OpenFlag_Create
|osl_File_OpenFlag_Write
);
73 // css::io::XOutputStream
74 virtual void SAL_CALL
writeBytes( const css::uno::Sequence
< sal_Int8
>& aData
) override
;
75 virtual void SAL_CALL
flush( ) override
;
76 virtual void SAL_CALL
closeOutput( ) override
;
79 void SAL_CALL
OslOutputStreamWrapper::writeBytes( const css::uno::Sequence
< sal_Int8
>& aData
)
81 sal_uInt64 uBytesToWrite
= aData
.getLength();
82 sal_uInt64 uBytesWritten
= 0;
84 sal_Int8
const * pBuffer
= aData
.getConstArray();
86 while( uBytesToWrite
)
88 osl::File::RC eRC
= maFile
.write( pBuffer
, uBytesToWrite
, uBytesWritten
);
92 case osl::File::E_INVAL
: // the format of the parameters was not valid
93 case osl::File::E_FBIG
: // File too large
95 case osl::File::E_AGAIN
: // Operation would block
96 case osl::File::E_BADF
: // Bad file
97 case osl::File::E_FAULT
: // Bad address
98 case osl::File::E_INTR
: // function call was interrupted
99 case osl::File::E_IO
: // I/O error
100 case osl::File::E_NOLCK
: // No record locks available
101 case osl::File::E_NOLINK
: // Link has been severed
102 case osl::File::E_NOSPC
: // No space left on device
103 case osl::File::E_NXIO
: // No such device or address
104 throw css::io::IOException(); // TODO: Better error handling
108 uBytesToWrite
-= uBytesWritten
;
109 pBuffer
+= uBytesWritten
;
113 void SAL_CALL
OslOutputStreamWrapper::flush( )
117 void SAL_CALL
OslOutputStreamWrapper::closeOutput( )
119 osl::File::RC eRC
= maFile
.close();
123 case osl::File::E_INVAL
: // the format of the parameters was not valid
125 case osl::File::E_BADF
: // Bad file
126 case osl::File::E_INTR
: // function call was interrupted
127 case osl::File::E_NOLINK
: // Link has been severed
128 case osl::File::E_NOSPC
: // No space left on device
129 case osl::File::E_IO
: // I/O error
130 throw css::io::IOException(); // TODO: Better error handling
136 class FlashExportFilter
: public cppu::WeakImplHelper
138 css::document::XFilter
,
139 css::document::XExporter
,
140 css::lang::XInitialization
,
141 css::lang::XServiceInfo
144 Reference
< XComponent
> mxDoc
;
145 Reference
< XComponentContext
> mxContext
;
146 Reference
< XStatusIndicator
> mxStatusIndicator
;
148 // #i56084# variables for selection export
149 Reference
< XShapes
> mxSelectedShapes
;
150 Reference
< XDrawPage
> mxSelectedDrawPage
;
151 bool mbExportSelection
;
154 explicit FlashExportFilter( const Reference
< XComponentContext
> &rxContext
);
157 virtual sal_Bool SAL_CALL
filter( const Sequence
< PropertyValue
>& aDescriptor
) override
;
159 void ExportAsMultipleFiles( const Sequence
< PropertyValue
>& aDescriptor
);
160 void ExportAsSingleFile( const Sequence
< PropertyValue
>& aDescriptor
);
162 virtual void SAL_CALL
cancel( ) override
;
165 virtual void SAL_CALL
setSourceDocument( const Reference
< XComponent
>& xDoc
) override
;
168 virtual void SAL_CALL
initialize( const Sequence
< Any
>& aArguments
) override
;
171 virtual OUString SAL_CALL
getImplementationName() override
;
172 virtual sal_Bool SAL_CALL
supportsService( const OUString
& ServiceName
) override
;
173 virtual Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
176 FlashExportFilter::FlashExportFilter(const Reference
< XComponentContext
> &rxContext
)
178 , mxContext(rxContext
)
179 , mxStatusIndicator()
181 , mxSelectedDrawPage()
182 , mbExportSelection(false)
186 static OUString
exportBackground(FlashExporter
&aFlashExporter
, const Reference
< XDrawPage
>& xDrawPage
, const OUString
& sPath
, sal_uInt32 nPage
, const char* suffix
)
188 OUString filename
= "slide" + OUString::number(nPage
+1) + OUString::createFromAscii(suffix
) + ".swf";
189 OUString fullpath
= sPath
+ "/" + filename
;
191 // AS: If suffix is "o" then the last parameter is true (for exporting objects).
192 Reference
<XOutputStream
> xOutputStreamWrap(*(new OslOutputStreamWrapper(fullpath
)), UNO_QUERY
);
193 sal_uInt16 nCached
= aFlashExporter
.exportBackgrounds( xDrawPage
, xOutputStreamWrap
, sal::static_int_cast
<sal_uInt16
>( nPage
), *suffix
== 'o' );
194 aFlashExporter
.Flush();
195 xOutputStreamWrap
.clear();
197 if (nCached
!= nPage
)
199 osl_removeFile(fullpath
.pData
);
200 if ( 0xffff == nCached
)
203 return "slide" + OUString::number(nCached
+1) + OUString::createFromAscii(suffix
) + ".swf";
209 template <typename TYPE
>
210 static TYPE
findPropertyValue(const Sequence
< PropertyValue
>& aPropertySequence
, const sal_Char
* name
, TYPE def
)
214 sal_Int32 nLength
= aPropertySequence
.getLength();
215 const PropertyValue
* pValue
= aPropertySequence
.getConstArray();
217 for ( sal_Int32 i
= 0 ; i
< nLength
; i
++)
219 if ( pValue
[i
].Name
.equalsAsciiL ( name
, strlen(name
) ) )
221 pValue
[i
].Value
>>= temp
;
229 sal_Bool SAL_CALL
FlashExportFilter::filter( const css::uno::Sequence
< css::beans::PropertyValue
>& aDescriptor
)
231 mxStatusIndicator
= findPropertyValue
<Reference
<XStatusIndicator
> >(aDescriptor
, "StatusIndicator", mxStatusIndicator
);
233 Sequence
< PropertyValue
> aFilterData
;
234 aFilterData
= findPropertyValue
<Sequence
< PropertyValue
> >(aDescriptor
, "FilterData", aFilterData
);
236 // #i56084# check if selection shall be exported only; if yes, get the selected page and the selection itself
237 if(findPropertyValue
<bool>(aDescriptor
, "SelectionOnly", false))
239 Reference
< XDesktop2
> xDesktop(Desktop::create(mxContext
));
243 Reference
< XFrame
> xFrame(xDesktop
->getCurrentFrame());
247 Reference
< XController
> xController(xFrame
->getController());
251 Reference
< XDrawView
> xDrawView(xController
, UNO_QUERY
);
255 mxSelectedDrawPage
= xDrawView
->getCurrentPage();
258 if(mxSelectedDrawPage
.is())
260 Reference
< XSelectionSupplier
> xSelection(xController
, UNO_QUERY
);
264 xSelection
->getSelection() >>= mxSelectedShapes
;
272 if(mxSelectedDrawPage
.is() && mxSelectedShapes
.is() && mxSelectedShapes
->getCount())
274 // #i56084# to export selection we need the selected page and the selected shapes.
275 // There must be shapes selected, else fallback to regular export (export all)
276 mbExportSelection
= true;
279 // #i56084# no multiple files (suppress) when selection since selection can only export a single page
280 if (!mbExportSelection
&& findPropertyValue
<bool>(aFilterData
, "ExportMultipleFiles", false ))
282 ExportAsMultipleFiles(aDescriptor
);
286 ExportAsSingleFile(aDescriptor
);
289 if( mxStatusIndicator
.is() )
290 mxStatusIndicator
->end();
296 // AS: When exporting as multiple files, each background, object layer, and slide gets its own
297 // file. Additionally, a file called BackgroundConfig.txt is generated, indicating which
298 // background and objects (if any) go with each slide. The files are named slideNb.swf,
299 // slideNo.swf, and slideNp.swf, where N is the slide number, and b=background, o=objects, and
300 // p=slide contents. Note that under normal circumstances, there will be very few b and o files.
302 // AS: HACK! Right now, I create a directory as a sibling to the swf file selected in the Export
303 // dialog. This directory is called presentation.sxi-swf-files. The name of the swf file selected
304 // in the Export dialog has no impact on this. All files created are placed in this directory.
305 void FlashExportFilter::ExportAsMultipleFiles(const Sequence
< PropertyValue
>& aDescriptor
)
307 Reference
< XDrawPagesSupplier
> xDrawPagesSupplier(mxDoc
, UNO_QUERY
);
308 if(!xDrawPagesSupplier
.is())
311 Reference
< XIndexAccess
> xDrawPages
= xDrawPagesSupplier
->getDrawPages();
315 Reference
< XDesktop2
> rDesktop
= Desktop::create( mxContext
);
317 Reference
< XStorable
> xStorable(rDesktop
->getCurrentComponent(), UNO_QUERY
);
321 Reference
< XDrawPage
> xDrawPage
;
323 Reference
< XFrame
> rFrame
= rDesktop
->getCurrentFrame();
324 Reference
< XDrawView
> rDrawView( rFrame
->getController(), UNO_QUERY
);
326 Reference
< XDrawPage
> rCurrentPage
= rDrawView
->getCurrentPage();
328 Sequence
< PropertyValue
> aFilterData
;
330 aFilterData
= findPropertyValue
<Sequence
< PropertyValue
> >(aDescriptor
, "FilterData", aFilterData
);
332 //AS: Do a bunch of path mangling to figure out where to put the files.
334 OUString sOriginalPath
= findPropertyValue
<OUString
>(aDescriptor
, "URL", OUString());
336 // AS: sPath is the parent directory, where everything else exists (like the sxi,
337 // the -swf-files folder, the -audio files, etc.
338 sal_Int32 lastslash
= sOriginalPath
.lastIndexOf('/');
339 OUString
sPath( sOriginalPath
.copy(0, lastslash
) );
341 OUString
sPresentation(xStorable
->getLocation());
343 lastslash
= sPresentation
.lastIndexOf('/') + 1;
344 sal_Int32 lastdot
= sPresentation
.lastIndexOf('.');
346 // AS: The name of the presentation, without 3 character extension.
347 OUString sPresentationName
;
348 if (lastdot
< 0) // fdo#71309 in case file has no name
349 sPresentationName
= sPresentation
.copy(lastslash
);
351 sPresentationName
= sPresentation
.copy(lastslash
, lastdot
- lastslash
);
353 OUString fullpath
, swfdirpath
, backgroundfilename
, objectsfilename
;
355 swfdirpath
= sPath
+ "/" + sPresentationName
+ ".sxi-swf-files";
357 osl_createDirectory( swfdirpath
.pData
);
359 fullpath
= swfdirpath
+ "/backgroundconfig.txt";
361 oslFileHandle
aBackgroundConfig( nullptr );
363 // AS: Only export the background config if we're exporting all of the pages, otherwise we'll
365 bool bExportAll
= findPropertyValue
<bool>(aFilterData
, "ExportAll", true);
368 osl_removeFile(fullpath
.pData
);
369 osl_openFile( fullpath
.pData
, &aBackgroundConfig
, osl_File_OpenFlag_Create
| osl_File_OpenFlag_Write
);
371 sal_uInt64 bytesWritten
;
372 osl_writeFile(aBackgroundConfig
, "slides=", strlen("slides="), &bytesWritten
);
375 // TODO: check for errors
377 FlashExporter
aFlashExporter(
381 findPropertyValue
<sal_Int32
>(aFilterData
, "CompressMode", 75),
382 findPropertyValue
<bool>(aFilterData
, "ExportOLEAsJPEG", false));
384 const sal_Int32 nPageCount
= xDrawPages
->getCount();
385 if ( mxStatusIndicator
.is() )
386 mxStatusIndicator
->start( "Saving :", nPageCount
);
388 for(sal_Int32 nPage
= 0; nPage
< nPageCount
; nPage
++)
390 if ( mxStatusIndicator
.is() )
391 mxStatusIndicator
->setValue( nPage
);
392 xDrawPages
->getByIndex(nPage
) >>= xDrawPage
;
394 // AS: If we're only exporting the current page, then skip the rest.
395 if (!bExportAll
&& xDrawPage
!= rCurrentPage
)
398 // AS: Export the background, the background objects, and then the slide contents.
399 if (bExportAll
|| findPropertyValue
<bool>(aFilterData
, "ExportBackgrounds", true))
401 backgroundfilename
= exportBackground(aFlashExporter
, xDrawPage
, swfdirpath
, nPage
, "b");
404 if (bExportAll
|| findPropertyValue
<bool>(aFilterData
, "ExportBackgroundObjects", true))
406 objectsfilename
= exportBackground(aFlashExporter
, xDrawPage
, swfdirpath
, nPage
, "o");
409 if (bExportAll
|| findPropertyValue
<bool>(aFilterData
, "ExportSlideContents", true))
411 fullpath
= swfdirpath
+ "/slide"+ OUString::number(nPage
+1) + "p.swf";
413 Reference
<XOutputStream
> xOutputStreamWrap(*(new OslOutputStreamWrapper(fullpath
)), UNO_QUERY
);
414 bool ret
= aFlashExporter
.exportSlides( xDrawPage
, xOutputStreamWrap
);
415 aFlashExporter
.Flush();
416 xOutputStreamWrap
.clear();
419 osl_removeFile(fullpath
.pData
);
422 // AS: Write out to the background config what backgrounds and objects this
426 OUString temp
= backgroundfilename
+ "|" + objectsfilename
;
427 OString
ASCIItemp(temp
.getStr(), temp
.getLength(), RTL_TEXTENCODING_ASCII_US
);
429 sal_uInt64 bytesWritten
;
430 osl_writeFile(aBackgroundConfig
, ASCIItemp
.getStr(), ASCIItemp
.getLength(), &bytesWritten
);
432 if (nPage
< nPageCount
- 1)
433 osl_writeFile(aBackgroundConfig
, "|", 1, &bytesWritten
);
438 osl_closeFile(aBackgroundConfig
);
441 void FlashExportFilter::ExportAsSingleFile(const Sequence
< PropertyValue
>& aDescriptor
)
443 Reference
< XOutputStream
> xOutputStream
= findPropertyValue
<Reference
<XOutputStream
> >(aDescriptor
, "OutputStream", nullptr);
444 Sequence
< PropertyValue
> aFilterData
;
446 if (!xOutputStream
.is() )
448 OSL_ASSERT ( false );
452 FlashExporter
aFlashExporter(
456 findPropertyValue
<sal_Int32
>(aFilterData
, "CompressMode", 75),
457 findPropertyValue
<bool>(aFilterData
, "ExportOLEAsJPEG", false));
459 aFlashExporter
.exportAll( mxDoc
, xOutputStream
, mxStatusIndicator
);
463 void SAL_CALL
FlashExportFilter::cancel( )
469 void SAL_CALL
FlashExportFilter::setSourceDocument( const css::uno::Reference
< css::lang::XComponent
>& xDoc
)
476 void SAL_CALL
FlashExportFilter::initialize( const css::uno::Sequence
< css::uno::Any
>& /* aArguments */ )
480 OUString
FlashExportFilter_getImplementationName ()
482 return "com.sun.star.comp.Impress.FlashExportFilter";
485 Sequence
< OUString
> FlashExportFilter_getSupportedServiceNames( )
487 Sequence
<OUString
> aRet
{ "com.sun.star.document.ExportFilter" };
491 Reference
< XInterface
> FlashExportFilter_createInstance( const Reference
< XMultiServiceFactory
> & rSMgr
)
493 return static_cast<cppu::OWeakObject
*>(new FlashExportFilter( comphelper::getComponentContext(rSMgr
) ));
497 OUString SAL_CALL
FlashExportFilter::getImplementationName( )
499 return FlashExportFilter_getImplementationName();
502 sal_Bool SAL_CALL
FlashExportFilter::supportsService( const OUString
& rServiceName
)
504 return cppu::supportsService( this, rServiceName
);
507 css::uno::Sequence
< OUString
> SAL_CALL
FlashExportFilter::getSupportedServiceNames( )
509 return FlashExportFilter_getSupportedServiceNames();
514 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */