handle Keynote package format too

Change-Id: I3023bcba0a3d3bd83aca56e4ef72a892da5b55cf
This commit is contained in:
David Tardon
2013-12-09 20:11:43 +01:00
parent 98bda6b40e
commit 735dc598cd
5 changed files with 417 additions and 15 deletions

View File

@@ -35,6 +35,7 @@ $(eval $(call gb_Library_use_libraries,wpftimpress,\
sal \
sot \
tl \
ucbhelper \
utl \
xo \
$(gb_UWINAPI) \

View File

@@ -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 \
))

View File

@@ -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: */

View File

@@ -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: */

View File

@@ -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;
}