/* -*- 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::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; 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 ) override; virtual void SAL_CALL flush( ) override; virtual void SAL_CALL closeOutput( ) override; }; void SAL_CALL OslOutputStreamWrapper::writeBytes( const css::uno::Sequence< sal_Int8 >& aData ) { 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( ) { } void SAL_CALL OslOutputStreamWrapper::closeOutput( ) { 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 ) override; void ExportAsMultipleFiles( const Sequence< PropertyValue >& aDescriptor ); void ExportAsSingleFile( const Sequence< PropertyValue >& aDescriptor ); virtual void SAL_CALL cancel( ) override; // XExporter virtual void SAL_CALL setSourceDocument( const Reference< XComponent >& xDoc ) override; // XInitialization virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; // XServiceInfo virtual OUString SAL_CALL getImplementationName() override; virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; }; FlashExportFilter::FlashExportFilter(const Reference< XComponentContext > &rxContext) : mxDoc() , mxContext(rxContext) , mxStatusIndicator() , mxSelectedShapes() , mxSelectedDrawPage() , mbExportSelection(false) { } static OUString exportBackground(FlashExporter &aFlashExporter, const Reference< XDrawPage >& xDrawPage, const OUString& sPath, sal_uInt32 nPage, const char* suffix) { OUString filename = "slide" + OUString::number(nPage+1) + OUString::createFromAscii(suffix) + ".swf"; OUString fullpath = sPath + "/" + 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 OUString("NULL"); else return "slide" + OUString::number(nCached+1) + OUString::createFromAscii(suffix) + ".swf"; } return filename; } template static 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 ) { 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", 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()) { xSelection->getSelection() >>= 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 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. void FlashExportFilter::ExportAsMultipleFiles(const Sequence< PropertyValue >& aDescriptor) { Reference< XDrawPagesSupplier > xDrawPagesSupplier(mxDoc, UNO_QUERY); if(!xDrawPagesSupplier.is()) return; Reference< XIndexAccess > xDrawPages( xDrawPagesSupplier->getDrawPages(), UNO_QUERY ); if(!xDrawPages.is()) return; Reference< XDesktop2 > rDesktop = Desktop::create( mxContext ); Reference< XStorable > xStorable(rDesktop->getCurrentComponent(), UNO_QUERY); if (!xStorable.is()) return; 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 + "/" + sPresentationName + ".sxi-swf-files"; oslFileError err; err = osl_createDirectory( swfdirpath.pData ); fullpath = swfdirpath + "/backgroundconfig.txt"; oslFileHandle aBackgroundConfig( 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, &aBackgroundConfig, osl_File_OpenFlag_Create | osl_File_OpenFlag_Write ); sal_uInt64 bytesWritten; err = osl_writeFile(aBackgroundConfig, "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 + "/slide"+ OUString::number(nPage+1) + "p.swf"; Reference xOutputStreamWrap(*(new OslOutputStreamWrapper(fullpath)), UNO_QUERY); bool ret = aFlashExporter.exportSlides( xDrawPage, xOutputStreamWrap ); 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 + "|" + objectsfilename; OString ASCIItemp(temp.getStr(), temp.getLength(), RTL_TEXTENCODING_ASCII_US); sal_uInt64 bytesWritten; osl_writeFile(aBackgroundConfig, ASCIItemp.getStr(), ASCIItemp.getLength(), &bytesWritten); if (nPage < nPageCount - 1) osl_writeFile(aBackgroundConfig, "|", 1, &bytesWritten); } } if (bExportAll) osl_closeFile(aBackgroundConfig); } void FlashExportFilter::ExportAsSingleFile(const Sequence< PropertyValue >& aDescriptor) { Reference < XOutputStream > xOutputStream = findPropertyValue >(aDescriptor, "OutputStream", nullptr); Sequence< PropertyValue > aFilterData; if (!xOutputStream.is() ) { OSL_ASSERT ( false ); return; } FlashExporter aFlashExporter( mxContext, mxSelectedShapes, mxSelectedDrawPage, findPropertyValue(aFilterData, "CompressMode", 75), findPropertyValue(aFilterData, "ExportOLEAsJPEG", false)); aFlashExporter.exportAll( mxDoc, xOutputStream, mxStatusIndicator ); } void SAL_CALL FlashExportFilter::cancel( ) { } // XExporter void SAL_CALL FlashExportFilter::setSourceDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) { mxDoc = xDoc; } // XInitialization void SAL_CALL FlashExportFilter::initialize( const css::uno::Sequence< css::uno::Any >& /* aArguments */ ) { } OUString FlashExportFilter_getImplementationName () { return OUString ( "com.sun.star.comp.Impress.FlashExportFilter" ); } Sequence< OUString > FlashExportFilter_getSupportedServiceNames( ) { Sequence aRet { "com.sun.star.document.ExportFilter" }; return aRet; } Reference< XInterface > FlashExportFilter_createInstance( const Reference< XMultiServiceFactory > & rSMgr) { return static_cast(new FlashExportFilter( comphelper::getComponentContext(rSMgr) )); } // XServiceInfo OUString SAL_CALL FlashExportFilter::getImplementationName( ) { return FlashExportFilter_getImplementationName(); } sal_Bool SAL_CALL FlashExportFilter::supportsService( const OUString& rServiceName ) { return cppu::supportsService( this, rServiceName ); } css::uno::Sequence< OUString > SAL_CALL FlashExportFilter::getSupportedServiceNames( ) { return FlashExportFilter_getSupportedServiceNames(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */