/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "swfexporter.hxx" #include "swfuno.hxx" #include using namespace ::com::sun::star::uno; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::drawing; using namespace ::com::sun::star::presentation; using namespace ::com::sun::star::task; using namespace ::com::sun::star::view; using ::com::sun::star::lang::XComponent; using ::com::sun::star::beans::PropertyValue; using ::com::sun::star::io::XOutputStream; using ::com::sun::star::container::XIndexAccess; using ::osl::FileBase; using ::com::sun::star::frame::XModel; namespace swf { class OslOutputStreamWrapper : public ::cppu::WeakImplHelper { osl::File mrFile; public: explicit OslOutputStreamWrapper(const OUString& rFileName) : mrFile(rFileName) { osl_removeFile(rFileName.pData); mrFile.open( osl_File_OpenFlag_Create|osl_File_OpenFlag_Write ); } // css::io::XOutputStream virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) throw (css::io::NotConnectedException, css::io::BufferSizeExceededException, css::io::IOException, css::uno::RuntimeException, std::exception) override; virtual void SAL_CALL flush( ) throw (css::io::NotConnectedException, css::io::BufferSizeExceededException, css::io::IOException, css::uno::RuntimeException, std::exception) override; virtual void SAL_CALL closeOutput( ) throw (css::io::NotConnectedException, css::io::BufferSizeExceededException, css::io::IOException, css::uno::RuntimeException, std::exception) override; }; void SAL_CALL OslOutputStreamWrapper::writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) throw (css::io::NotConnectedException, css::io::BufferSizeExceededException, css::io::IOException, css::uno::RuntimeException, std::exception) { sal_uInt64 uBytesToWrite = aData.getLength(); sal_uInt64 uBytesWritten = 0; sal_Int8 const * pBuffer = aData.getConstArray(); while( uBytesToWrite ) { osl::File::RC eRC = mrFile.write( pBuffer, uBytesToWrite, uBytesWritten); switch( eRC ) { case osl::File::E_INVAL: // the format of the parameters was not valid case osl::File::E_FBIG: // File too large case osl::File::E_AGAIN: // Operation would block case osl::File::E_BADF: // Bad file case osl::File::E_FAULT: // Bad address case osl::File::E_INTR: // function call was interrupted case osl::File::E_IO: // I/O error case osl::File::E_NOLCK: // No record locks available case osl::File::E_NOLINK: // Link has been severed case osl::File::E_NOSPC: // No space left on device case osl::File::E_NXIO: // No such device or address throw css::io::IOException(); // TODO: Better error handling default: break; } uBytesToWrite -= uBytesWritten; pBuffer += uBytesWritten; } } void SAL_CALL OslOutputStreamWrapper::flush( ) throw (css::io::NotConnectedException, css::io::BufferSizeExceededException, css::io::IOException, css::uno::RuntimeException, std::exception) { } void SAL_CALL OslOutputStreamWrapper::closeOutput( ) throw (css::io::NotConnectedException, css::io::BufferSizeExceededException, css::io::IOException, css::uno::RuntimeException, std::exception) { osl::File::RC eRC = mrFile.close(); switch( eRC ) { case osl::File::E_INVAL: // the format of the parameters was not valid case osl::File::E_BADF: // Bad file case osl::File::E_INTR: // function call was interrupted case osl::File::E_NOLINK: // Link has been severed case osl::File::E_NOSPC: // No space left on device case osl::File::E_IO: // I/O error throw css::io::IOException(); // TODO: Better error handling default: break; } } class FlashExportFilter : public cppu::WeakImplHelper < css::document::XFilter, css::document::XExporter, css::lang::XInitialization, css::lang::XServiceInfo > { Reference< XComponent > mxDoc; Reference< XComponentContext > mxContext; Reference< XStatusIndicator> mxStatusIndicator; // #i56084# variables for selection export Reference< XShapes > mxSelectedShapes; Reference< XDrawPage > mxSelectedDrawPage; bool mbExportSelection; public: explicit FlashExportFilter( const Reference< XComponentContext > &rxContext); // XFilter virtual sal_Bool SAL_CALL filter( const Sequence< PropertyValue >& aDescriptor ) throw(RuntimeException, std::exception) override; bool ExportAsMultipleFiles( const Sequence< PropertyValue >& aDescriptor ); bool ExportAsSingleFile( const Sequence< PropertyValue >& aDescriptor ); virtual void SAL_CALL cancel( ) throw (RuntimeException, std::exception) override; // XExporter virtual void SAL_CALL setSourceDocument( const Reference< XComponent >& xDoc ) throw(IllegalArgumentException, RuntimeException, std::exception) override; // XInitialization virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) throw(Exception, RuntimeException, std::exception) override; // XServiceInfo virtual OUString SAL_CALL getImplementationName() throw(RuntimeException, std::exception) override; virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw(RuntimeException, std::exception) override; virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() throw(RuntimeException, std::exception) override; }; FlashExportFilter::FlashExportFilter(const Reference< XComponentContext > &rxContext) : mxDoc() , mxContext(rxContext) , mxStatusIndicator() , mxSelectedShapes() , mxSelectedDrawPage() , mbExportSelection(false) { } OUString exportBackground(FlashExporter &aFlashExporter, const Reference< XDrawPage >& xDrawPage, const OUString& sPath, sal_uInt32 nPage, const char* suffix) { OUString filename = STR("slide") + VAL(nPage+1) + STR(suffix) + STR(".swf"); OUString fullpath = sPath + STR("/") + filename; // AS: If suffix is "o" then the last parameter is true (for exporting objects). Reference xOutputStreamWrap(*(new OslOutputStreamWrapper(fullpath)), UNO_QUERY); sal_uInt16 nCached = aFlashExporter.exportBackgrounds( xDrawPage, xOutputStreamWrap, sal::static_int_cast( nPage ), *suffix == 'o' ); aFlashExporter.Flush(); xOutputStreamWrap.clear(); if (nCached != nPage) { osl_removeFile(fullpath.pData); if ( 0xffff == nCached ) return STR("NULL"); else return STR("slide") + VAL(nCached+1) + STR(suffix) + STR(".swf"); } return filename; } template TYPE findPropertyValue(const Sequence< PropertyValue >& aPropertySequence, const sal_Char* name, TYPE def) { TYPE temp = TYPE(); sal_Int32 nLength = aPropertySequence.getLength(); const PropertyValue * pValue = aPropertySequence.getConstArray(); for ( sal_Int32 i = 0 ; i < nLength; i++) { if ( pValue[i].Name.equalsAsciiL ( name, strlen(name) ) ) { pValue[i].Value >>= temp; return temp; } } return def; } sal_Bool SAL_CALL FlashExportFilter::filter( const css::uno::Sequence< css::beans::PropertyValue >& aDescriptor ) throw (RuntimeException, std::exception) { mxStatusIndicator = findPropertyValue >(aDescriptor, "StatusIndicator", mxStatusIndicator); Sequence< PropertyValue > aFilterData; aFilterData = findPropertyValue >(aDescriptor, "FilterData", aFilterData); // #i56084# check if selection shall be exported only; if yes, get the selected page and the selection itself if(findPropertyValue(aDescriptor, "SelectionOnly", sal_False)) { Reference< XDesktop2 > xDesktop(Desktop::create(mxContext)); if(xDesktop.is()) { Reference< XFrame > xFrame(xDesktop->getCurrentFrame()); if(xFrame.is()) { Reference< XController > xController(xFrame->getController()); if(xController.is()) { Reference< XDrawView > xDrawView(xController, UNO_QUERY); if(xDrawView.is()) { mxSelectedDrawPage = xDrawView->getCurrentPage(); } if(mxSelectedDrawPage.is()) { Reference< XSelectionSupplier > xSelection(xController, UNO_QUERY); if(xSelection.is()) { Any aSelection; if(xSelection->getSelection() >>= aSelection) { aSelection >>= mxSelectedShapes; } } } } } } } if(mxSelectedDrawPage.is() && mxSelectedShapes.is() && mxSelectedShapes->getCount()) { // #i56084# to export selection we need the selected page and the selected shapes. // There must be shapes selected, else fallback to regular export (export all) mbExportSelection = true; } // #i56084# no multiple files (suppress) when selection since selection can only export a single page if (!mbExportSelection && findPropertyValue(aFilterData, "ExportMultipleFiles", false )) { ExportAsMultipleFiles(aDescriptor); } else { ExportAsSingleFile(aDescriptor); } if( mxStatusIndicator.is() ) mxStatusIndicator->end(); return sal_True; } // AS: When exporting as multiple files, each background, object layer, and slide gets its own // file. Additionally, a file called BackgroundConfig.txt is generated, indicating which // background and objects (if any) go with each slide. The files are named slideNb.swf, // slideNo.swf, and slideNp.swf, where N is the slide number, and b=background, o=objects, and // p=slide contents. Note that under normal circumstances, there will be very few b and o files. // AS: HACK! Right now, I create a directory as a sibling to the swf file selected in the Export // dialog. This directory is called presentation.sxi-swf-files. The name of the swf file selected // in the Export dialog has no impact on this. All files created are placed in this directory. bool FlashExportFilter::ExportAsMultipleFiles(const Sequence< PropertyValue >& aDescriptor) { Reference< XDrawPagesSupplier > xDrawPagesSupplier(mxDoc, UNO_QUERY); if(!xDrawPagesSupplier.is()) return false; Reference< XIndexAccess > xDrawPages( xDrawPagesSupplier->getDrawPages(), UNO_QUERY ); if(!xDrawPages.is()) return false; Reference< XDesktop2 > rDesktop = Desktop::create( mxContext ); Reference< XStorable > xStorable(rDesktop->getCurrentComponent(), UNO_QUERY); if (!xStorable.is()) return false; Reference< XDrawPage > xDrawPage; Reference< XFrame > rFrame = rDesktop->getCurrentFrame(); Reference< XDrawView > rDrawView( rFrame->getController(), UNO_QUERY ); Reference< XDrawPage > rCurrentPage = rDrawView->getCurrentPage(); Sequence< PropertyValue > aFilterData; aFilterData = findPropertyValue >(aDescriptor, "FilterData", aFilterData); //AS: Do a bunch of path mangling to figure out where to put the files. OUString sOriginalPath = findPropertyValue(aDescriptor, "URL", OUString()); // AS: sPath is the parent directory, where everything else exists (like the sxi, // the -swf-files folder, the -audio files, etc. sal_Int32 lastslash = sOriginalPath.lastIndexOf('/'); OUString sPath( sOriginalPath.copy(0, lastslash) ); OUString sPresentation(xStorable->getLocation()); lastslash = sPresentation.lastIndexOf('/') + 1; sal_Int32 lastdot = sPresentation.lastIndexOf('.'); // AS: The name of the presentation, without 3 character extension. OUString sPresentationName; if (lastdot < 0) // fdo#71309 in case file has no name sPresentationName = sPresentation.copy(lastslash); else sPresentationName = sPresentation.copy(lastslash, lastdot - lastslash); OUString fullpath, swfdirpath, backgroundfilename, objectsfilename; swfdirpath = sPath + STR("/") + sPresentationName + STR(".sxi-swf-files"); oslFileError err; err = osl_createDirectory( swfdirpath.pData ); fullpath = swfdirpath + STR("/backgroundconfig.txt"); oslFileHandle xBackgroundConfig( nullptr ); // AS: Only export the background config if we're exporting all of the pages, otherwise we'll // screw it up. bool bExportAll = findPropertyValue(aFilterData, "ExportAll", true); if (bExportAll) { osl_removeFile(fullpath.pData); osl_openFile( fullpath.pData, &xBackgroundConfig, osl_File_OpenFlag_Create | osl_File_OpenFlag_Write ); sal_uInt64 bytesWritten; err = osl_writeFile(xBackgroundConfig, "slides=", strlen("slides="), &bytesWritten); } // TODO: check for errors (void) err; FlashExporter aFlashExporter( mxContext, mxSelectedShapes, mxSelectedDrawPage, findPropertyValue(aFilterData, "CompressMode", 75), findPropertyValue(aFilterData, "ExportOLEAsJPEG", false)); const sal_Int32 nPageCount = xDrawPages->getCount(); if ( mxStatusIndicator.is() ) mxStatusIndicator->start( "Saving :", nPageCount); for(sal_Int32 nPage = 0; nPage < nPageCount; nPage++) { if ( mxStatusIndicator.is() ) mxStatusIndicator->setValue( nPage ); xDrawPages->getByIndex(nPage) >>= xDrawPage; // AS: If we're only exporting the current page, then skip the rest. if (!bExportAll && xDrawPage != rCurrentPage) continue; // AS: Export the background, the background objects, and then the slide contents. if (bExportAll || findPropertyValue(aFilterData, "ExportBackgrounds", true)) { backgroundfilename = exportBackground(aFlashExporter, xDrawPage, swfdirpath, nPage, "b"); } if (bExportAll || findPropertyValue(aFilterData, "ExportBackgroundObjects", true)) { objectsfilename = exportBackground(aFlashExporter, xDrawPage, swfdirpath, nPage, "o"); } if (bExportAll || findPropertyValue(aFilterData, "ExportSlideContents", true)) { fullpath = swfdirpath + STR("/slide") + VAL(nPage+1) + STR("p.swf"); Reference xOutputStreamWrap(*(new OslOutputStreamWrapper(fullpath)), UNO_QUERY); bool ret = aFlashExporter.exportSlides( xDrawPage, xOutputStreamWrap, sal::static_int_cast( nPage ) ); aFlashExporter.Flush(); xOutputStreamWrap.clear(); if (!ret) osl_removeFile(fullpath.pData); } // AS: Write out to the background config what backgrounds and objects this // slide used. if (bExportAll) { OUString temp = backgroundfilename + STR("|") + objectsfilename; OString ASCIItemp(temp.getStr(), temp.getLength(), RTL_TEXTENCODING_ASCII_US); sal_uInt64 bytesWritten; osl_writeFile(xBackgroundConfig, ASCIItemp.getStr(), ASCIItemp.getLength(), &bytesWritten); if (nPage < nPageCount - 1) osl_writeFile(xBackgroundConfig, "|", 1, &bytesWritten); } } if (bExportAll) osl_closeFile(xBackgroundConfig); return true; } bool FlashExportFilter::ExportAsSingleFile(const Sequence< PropertyValue >& aDescriptor) { Reference < XOutputStream > xOutputStream = findPropertyValue >(aDescriptor, "OutputStream", nullptr); Sequence< PropertyValue > aFilterData; if (!xOutputStream.is() ) { OSL_ASSERT ( false ); return false; } FlashExporter aFlashExporter( mxContext, mxSelectedShapes, mxSelectedDrawPage, findPropertyValue(aFilterData, "CompressMode", 75), findPropertyValue(aFilterData, "ExportOLEAsJPEG", false)); return aFlashExporter.exportAll( mxDoc, xOutputStream, mxStatusIndicator ); } void SAL_CALL FlashExportFilter::cancel( ) throw (RuntimeException, std::exception) { } // XExporter void SAL_CALL FlashExportFilter::setSourceDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) throw (css::lang::IllegalArgumentException, RuntimeException, std::exception) { mxDoc = xDoc; } // XInitialization void SAL_CALL FlashExportFilter::initialize( const css::uno::Sequence< css::uno::Any >& /* aArguments */ ) throw (Exception, RuntimeException, std::exception) { } OUString FlashExportFilter_getImplementationName () throw (RuntimeException) { return OUString ( "com.sun.star.comp.Impress.FlashExportFilter" ); } Sequence< OUString > SAL_CALL FlashExportFilter_getSupportedServiceNames( ) throw (RuntimeException) { Sequence aRet { "com.sun.star.document.ExportFilter" }; return aRet; } Reference< XInterface > SAL_CALL FlashExportFilter_createInstance( const Reference< XMultiServiceFactory > & rSMgr) throw( Exception ) { return static_cast(new FlashExportFilter( comphelper::getComponentContext(rSMgr) )); } // XServiceInfo OUString SAL_CALL FlashExportFilter::getImplementationName( ) throw (RuntimeException, std::exception) { return FlashExportFilter_getImplementationName(); } sal_Bool SAL_CALL FlashExportFilter::supportsService( const OUString& rServiceName ) throw (RuntimeException, std::exception) { return cppu::supportsService( this, rServiceName ); } css::uno::Sequence< OUString > SAL_CALL FlashExportFilter::getSupportedServiceNames( ) throw (RuntimeException, std::exception) { return FlashExportFilter_getSupportedServiceNames(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */