summaryrefslogtreecommitdiff
path: root/writerperfect
diff options
context:
space:
mode:
authorDavid Tardon <dtardon@redhat.com>2013-12-09 20:11:43 +0100
committerDavid Tardon <dtardon@redhat.com>2013-12-13 09:30:30 +0100
commit735dc598cd38c76aeeaa37df57f1d0018fe294c8 (patch)
tree4dd28221a2b311864cf06ca5236d6dc737b8860b /writerperfect
parent98bda6b40e21b4b9dff239ccfb6480de25d6393b (diff)
handle Keynote package format too
Change-Id: I3023bcba0a3d3bd83aca56e4ef72a892da5b55cf
Diffstat (limited to 'writerperfect')
-rw-r--r--writerperfect/Library_wpftimpress.mk1
-rw-r--r--writerperfect/StaticLibrary_writerperfect.mk1
-rw-r--r--writerperfect/source/common/DirectoryStream.cxx164
-rw-r--r--writerperfect/source/common/DirectoryStream.hxx48
-rw-r--r--writerperfect/source/impress/KeynoteImportFilter.cxx218
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;
}