diff options
author | David Tardon <dtardon@redhat.com> | 2013-12-09 20:11:43 +0100 |
---|---|---|
committer | David Tardon <dtardon@redhat.com> | 2013-12-13 09:30:30 +0100 |
commit | 735dc598cd38c76aeeaa37df57f1d0018fe294c8 (patch) | |
tree | 4dd28221a2b311864cf06ca5236d6dc737b8860b /writerperfect | |
parent | 98bda6b40e21b4b9dff239ccfb6480de25d6393b (diff) |
handle Keynote package format too
Change-Id: I3023bcba0a3d3bd83aca56e4ef72a892da5b55cf
Diffstat (limited to 'writerperfect')
-rw-r--r-- | writerperfect/Library_wpftimpress.mk | 1 | ||||
-rw-r--r-- | writerperfect/StaticLibrary_writerperfect.mk | 1 | ||||
-rw-r--r-- | writerperfect/source/common/DirectoryStream.cxx | 164 | ||||
-rw-r--r-- | writerperfect/source/common/DirectoryStream.hxx | 48 | ||||
-rw-r--r-- | writerperfect/source/impress/KeynoteImportFilter.cxx | 218 |
5 files changed, 417 insertions, 15 deletions
diff --git a/writerperfect/Library_wpftimpress.mk b/writerperfect/Library_wpftimpress.mk index cb69f98ca7a7..08cdd12c387a 100644 --- a/writerperfect/Library_wpftimpress.mk +++ b/writerperfect/Library_wpftimpress.mk @@ -35,6 +35,7 @@ $(eval $(call gb_Library_use_libraries,wpftimpress,\ sal \ sot \ tl \ + ucbhelper \ utl \ xo \ $(gb_UWINAPI) \ diff --git a/writerperfect/StaticLibrary_writerperfect.mk b/writerperfect/StaticLibrary_writerperfect.mk index d6b7ab537d3c..6b21c02ebf99 100644 --- a/writerperfect/StaticLibrary_writerperfect.mk +++ b/writerperfect/StaticLibrary_writerperfect.mk @@ -38,6 +38,7 @@ $(eval $(call gb_StaticLibrary_use_api,writerperfect,\ )) $(eval $(call gb_StaticLibrary_add_exception_objects,writerperfect,\ + writerperfect/source/common/DirectoryStream \ writerperfect/source/common/DocumentHandler \ writerperfect/source/common/WPXSvStream \ )) diff --git a/writerperfect/source/common/DirectoryStream.cxx b/writerperfect/source/common/DirectoryStream.cxx new file mode 100644 index 000000000000..14b37f37f221 --- /dev/null +++ b/writerperfect/source/common/DirectoryStream.cxx @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* writerperfect + * Version: MPL 2.0 / LGPLv2.1+ + * + * 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/. + * + * Major Contributor(s): + * Copyright (C) 2007 Fridrich Strba (fridrich.strba@bluewin.ch) + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU Lesser General Public License Version 2.1 or later + * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are + * applicable instead of those above. + * + * For further information visit http://libwpd.sourceforge.net + */ + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/ucb/XContent.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include <comphelper/processfactory.hxx> + +#include <rtl/ustring.hxx> + +#include <ucbhelper/content.hxx> + +#include "DirectoryStream.hxx" +#include "WPXSvStream.hxx" + +namespace io = com::sun::star::io; +namespace sdbc = com::sun::star::sdbc; +namespace ucb = com::sun::star::ucb; +namespace uno = com::sun::star::uno; + +namespace writerperfect +{ + +namespace +{ + +struct NotADirectoryException +{ +}; + +} + +namespace +{ + +uno::Reference<io::XInputStream> findStream(ucbhelper::Content &rContent, const rtl::OUString &rName) +{ + uno::Reference<io::XInputStream> xInputStream; + + uno::Sequence<rtl::OUString> lPropNames(1); + lPropNames[0] = "Title"; + try + { + const uno::Reference<sdbc::XResultSet> xResultSet( + rContent.createCursor(lPropNames, ucbhelper::INCLUDE_DOCUMENTS_ONLY)); + if (xResultSet->first()) + { + const uno::Reference<ucb::XContentAccess> xContentAccess(xResultSet, uno::UNO_QUERY_THROW); + const uno::Reference<sdbc::XRow> xRow(xResultSet, uno::UNO_QUERY_THROW); + do + { + const rtl::OUString aTitle(xRow->getString(1)); + if (aTitle == rName) + { + const uno::Reference<ucb::XContent> xSubContent(xContentAccess->queryContent()); + ucbhelper::Content aSubContent(xSubContent, uno::Reference<ucb::XCommandEnvironment>(), comphelper::getProcessComponentContext()); + xInputStream = aSubContent.openStream(); + break; + } + } while (xResultSet->next()); + } + } + catch (uno::RuntimeException) + { + // ignore + } + catch (uno::Exception) + { + // ignore + } + + return xInputStream; +} + +} + +struct DirectoryStream::Impl +{ + Impl(const uno::Reference<ucb::XContent> &rxContent); + + uno::Reference<ucb::XContent> xContent; +}; + +DirectoryStream::Impl::Impl(const uno::Reference<ucb::XContent> &rxContent) + : xContent(rxContent) +{ +} + +DirectoryStream::DirectoryStream(const com::sun::star::uno::Reference<com::sun::star::ucb::XContent> &xContent) + : m_pImpl(new Impl(xContent)) +{ +} + +DirectoryStream::~DirectoryStream() +{ + delete m_pImpl; +} + +bool DirectoryStream::isOLEStream() +{ + return true; +} + +WPXInputStream *DirectoryStream::getDocumentOLEStream(const char *const pName) +{ + WPXInputStream *input = 0; + + ucbhelper::Content aContent(m_pImpl->xContent, uno::Reference<ucb::XCommandEnvironment>(), comphelper::getProcessComponentContext()); + const uno::Reference<io::XInputStream> xInputStream(findStream(aContent, rtl::OUString::createFromAscii(pName))); + if (xInputStream.is()) + input = new WPXSvInputStream(xInputStream); + + return input; +} + +const unsigned char *DirectoryStream::read(const unsigned long, unsigned long &nNumBytesRead) +{ + nNumBytesRead = 0; + return 0; +} + +int DirectoryStream::seek(const long, const WPX_SEEK_TYPE) +{ + return -1; +} + +long DirectoryStream::tell() +{ + return 0; +} + +bool DirectoryStream::atEOS() +{ + return true; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/common/DirectoryStream.hxx b/writerperfect/source/common/DirectoryStream.hxx new file mode 100644 index 000000000000..8dc6efbea520 --- /dev/null +++ b/writerperfect/source/common/DirectoryStream.hxx @@ -0,0 +1,48 @@ +/* -*- 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/. + */ + +#ifndef DIRECTORYSTREAM_H_INCLUDED +#define DIRECTORYSTREAM_H_INCLUDED + +#include <libwpd-stream/libwpd-stream.h> + +#include <com/sun/star/uno/Reference.h> + +namespace com { namespace sun { namespace star { namespace ucb { + class XContent; +} } } } + +namespace writerperfect +{ + +class DirectoryStream : public WPXInputStream +{ + struct Impl; + +public: + explicit DirectoryStream(const com::sun::star::uno::Reference<com::sun::star::ucb::XContent> &xContent); + virtual ~DirectoryStream(); + + virtual bool isOLEStream(); + virtual WPXInputStream *getDocumentOLEStream(const char *pName); + + virtual const unsigned char *read(unsigned long nNumBytes, unsigned long &nNumBytesRead); + virtual int seek(long nOffset, WPX_SEEK_TYPE eSeekType); + virtual long tell(); + virtual bool atEOS(); + +private: + Impl *m_pImpl; +}; + +} + +#endif // DIRECTORYSTREAM_H_INCLUDED + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerperfect/source/impress/KeynoteImportFilter.cxx b/writerperfect/source/impress/KeynoteImportFilter.cxx index 0035d5df7a29..a63357c33f1f 100644 --- a/writerperfect/source/impress/KeynoteImportFilter.cxx +++ b/writerperfect/source/impress/KeynoteImportFilter.cxx @@ -7,10 +7,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <boost/shared_ptr.hpp> + #include <osl/diagnose.h> #include <rtl/tencinfo.h> +#include <comphelper/processfactory.hxx> +#include <comphelper/types.hxx> + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XChild.hpp> #include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/XContent.hpp> #include <com/sun/star/xml/sax/XAttributeList.hpp> #include <com/sun/star/xml/sax/XDocumentHandler.hpp> #include <com/sun/star/xml/sax/InputSource.hpp> @@ -18,17 +27,22 @@ #include <com/sun/star/io/XSeekable.hpp> #include <com/sun/star/uno/Reference.h> +#include <ucbhelper/content.hxx> + #include <xmloff/attrlist.hxx> #include <libetonyek/libetonyek.h> #include <libodfgen/libodfgen.hxx> +#include "common/DirectoryStream.hxx" #include "common/DocumentHandler.hxx" #include "common/WPXSvStream.hxx" #include "KeynoteImportFilter.hxx" #include <iostream> +using boost::shared_ptr; + using namespace ::com::sun::star::uno; using com::sun::star::uno::Reference; using com::sun::star::io::XInputStream; @@ -48,6 +62,46 @@ using com::sun::star::xml::sax::XAttributeList; using com::sun::star::xml::sax::XDocumentHandler; using com::sun::star::xml::sax::XParser; +namespace beans = com::sun::star::beans; +namespace container = com::sun::star::container; +namespace ucb = com::sun::star::ucb; + +namespace +{ + +template<class T> +sal_Bool lcl_queryIsPackage( const Sequence<T> &lComponentData ) +{ + sal_Bool bIsPackage = sal_False; + + const sal_Int32 nLength = lComponentData.getLength(); + const T *pValue = lComponentData.getConstArray(); + for ( sal_Int32 i = 0; i < nLength; ++i) + { + if ( pValue[i].Name == "IsPackage" ) + { + pValue[i].Value >>= bIsPackage; + break; + } + } + + return bIsPackage; +} + +sal_Bool lcl_isPackage( const Any &rComponentData ) +{ + Sequence < beans::NamedValue > lComponentDataNV; + Sequence < beans::PropertyValue > lComponentDataPV; + + if ( rComponentData >>= lComponentDataNV ) + return lcl_queryIsPackage( lComponentDataNV ); + else if ( rComponentData >>= lComponentDataPV ) + return lcl_queryIsPackage( lComponentDataPV ); + + return false; +} + +} sal_Bool SAL_CALL KeynoteImportFilter::filter( const Sequence< ::com::sun::star::beans::PropertyValue >& aDescriptor ) throw (RuntimeException) @@ -56,10 +110,16 @@ throw (RuntimeException) sal_Int32 nLength = aDescriptor.getLength(); const PropertyValue *pValue = aDescriptor.getConstArray(); Reference < XInputStream > xInputStream; + Reference < ucb::XContent > xContent; + sal_Bool bIsPackage = sal_False; for ( sal_Int32 i = 0 ; i < nLength; i++) { - if ( pValue[i].Name == "InputStream" ) + if ( pValue[i].Name == "ComponentData" ) + bIsPackage = lcl_isPackage( pValue[i].Value ); + else if ( pValue[i].Name == "InputStream" ) pValue[i].Value >>= xInputStream; + else if ( pValue[i].Name == "UCBContent" ) + pValue[i].Value >>= xContent; } if ( !xInputStream.is() ) { @@ -67,6 +127,12 @@ throw (RuntimeException) return sal_False; } + if ( bIsPackage && !xContent.is() ) + { + SAL_WARN("writerperfect", "the input claims to be a package, but does not have UCBContent"); + bIsPackage = false; + } + // An XML import service: what we push sax messages to.. Reference < XDocumentHandler > xInternalHandler( mxContext->getServiceManager()->createInstanceWithContext( @@ -81,10 +147,14 @@ throw (RuntimeException) // writes to in-memory target doc DocumentHandler xHandler(xInternalHandler); - WPXSvInputStream input( xInputStream ); + shared_ptr< WPXInputStream > input; + if ( bIsPackage ) + input.reset( new writerperfect::DirectoryStream( xContent ) ); + else + input.reset( new WPXSvInputStream( xInputStream ) ); OdpGenerator exporter(&xHandler, ODF_FLAT_XML); - bool tmpParseResult = libetonyek::KEYDocument::parse(&input, &exporter); + bool tmpParseResult = libetonyek::KEYDocument::parse(input.get(), &exporter); return tmpParseResult; } @@ -107,37 +177,155 @@ OUString SAL_CALL KeynoteImportFilter::detect( com::sun::star::uno::Sequence< Pr throw( com::sun::star::uno::RuntimeException ) { SAL_INFO("writerperfect", "KeynoteImportFilter::detect"); - OUString sTypeName; + sal_Int32 nLength = Descriptor.getLength(); - sal_Int32 location = nLength; + sal_Int32 nNewLength = nLength + 2; + sal_Int32 nComponentDataLocation = -1; + sal_Int32 nTypeNameLocation = -1; + sal_Int32 nUCBContentLocation = -1; + bool bIsPackage = false; + bool bUCBContentChanged = false; const PropertyValue *pValue = Descriptor.getConstArray(); Reference < XInputStream > xInputStream; + Reference < ucb::XContent > xContent; + Sequence < beans::NamedValue > lComponentDataNV; + Sequence < beans::PropertyValue > lComponentDataPV; + bool bComponentDataNV = true; + for ( sal_Int32 i = 0 ; i < nLength; i++) { if ( pValue[i].Name == "TypeName" ) - location=i; + { + nTypeNameLocation = i; + --nNewLength; + } + if ( pValue[i].Name == "ComponentData" ) + { + bComponentDataNV = pValue[i].Value >>= lComponentDataNV; + if (!bComponentDataNV) + pValue[i].Value >>= lComponentDataPV; + nComponentDataLocation = i; + --nNewLength; + } else if ( pValue[i].Name == "InputStream" ) + { pValue[i].Value >>= xInputStream; + } + else if ( pValue[i].Name == "UCBContent" ) + { + pValue[i].Value >>= xContent; + nUCBContentLocation = i; + } } + assert(nNewLength >= nLength); + if (!xInputStream.is()) return OUString(); - WPXSvInputStream input( xInputStream ); + shared_ptr< WPXInputStream > input( new WPXSvInputStream( xInputStream ) ); - if (libetonyek::KEYDocument::isSupported(&input)) - sTypeName = "impress_AppleKeynote"; - - if (!sTypeName.isEmpty()) + /* Apple Keynote documents come in two variants: + * * actual files (zip), only produced by Keynote 5 (at least with + * default settings) + * * packages (IOW, directories), produced by Keynote 1-4 and again + * starting with 6. + * But since the libetonyek import only works with a stream, we need + * to pass it one for the whole package. Here we determine if that + * is needed. + * + * Note: for convenience, we also recognize that the main XML file + * from a package was passed and pass the whole package to the + * filter instead. + */ + if ( xContent.is() ) { - if ( location == nLength ) + ucbhelper::Content aContent( xContent, Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + if ( aContent.isFolder() ) { - Descriptor.realloc(nLength+1); - Descriptor[location].Name = "TypeName"; + input.reset( new writerperfect::DirectoryStream( xContent ) ); + bIsPackage = true; } - Descriptor[location].Value <<=sTypeName; + libetonyek::KEYDocumentType type = libetonyek::KEY_DOCUMENT_TYPE_UNKNOWN; + if ( !libetonyek::KEYDocument::isSupported( input.get(), &type ) ) + return OUString(); + + if ( type == libetonyek::KEY_DOCUMENT_TYPE_APXL_FILE ) + { + assert( !bIsPackage ); + + const Reference < container::XChild > xChild( xContent, UNO_QUERY ); + if ( xChild.is() ) + { + const Reference < ucb::XContent > xPackageContent( xChild->getParent(), UNO_QUERY ); + if ( xPackageContent.is() ) + { + input.reset( new writerperfect::DirectoryStream( xPackageContent ) ); + if ( libetonyek::KEYDocument::isSupported( input.get() ) ) + { + xContent = xPackageContent; + bUCBContentChanged = true; + bIsPackage = true; + } + } + } + } } + + // we do not need to insert ComponentData if this is not a package + if ( !bIsPackage && ( nComponentDataLocation == -1 ) ) + --nNewLength; + + if ( nNewLength > nLength ) + Descriptor.realloc( nNewLength ); + + if ( nTypeNameLocation == -1 ) + { + assert( nLength < nNewLength ); + nTypeNameLocation = nLength++; + Descriptor[nTypeNameLocation].Name = "TypeName"; + } + + if ( bIsPackage && ( nComponentDataLocation == -1 ) ) + { + assert( nLength < nNewLength ); + nComponentDataLocation = nLength++; + Descriptor[nComponentDataLocation].Name = "ComponentData"; + } + + if ( bIsPackage ) + { + if (bComponentDataNV) + { + const sal_Int32 nCDSize = lComponentDataNV.getLength(); + lComponentDataNV.realloc( nCDSize + 1 ); + beans::NamedValue aValue; + aValue.Name = "IsPackage"; + aValue.Value = comphelper::makeBoolAny(sal_True); + lComponentDataNV[nCDSize] = aValue; + Descriptor[nComponentDataLocation].Value <<= lComponentDataNV; + } + else + { + const sal_Int32 nCDSize = lComponentDataPV.getLength(); + lComponentDataPV.realloc( nCDSize + 1 ); + beans::PropertyValue aProp; + aProp.Name = "IsPackage"; + aProp.Value = comphelper::makeBoolAny(sal_True); + aProp.Handle = -1; + aProp.State = beans::PropertyState_DIRECT_VALUE; + lComponentDataPV[nCDSize] = aProp; + Descriptor[nComponentDataLocation].Value <<= lComponentDataPV; + } + } + + if ( bUCBContentChanged ) + Descriptor[nUCBContentLocation].Value <<= xContent; + + const OUString sTypeName("impress_AppleKeynote"); + Descriptor[nTypeNameLocation].Value <<= sTypeName; + return sTypeName; } |