You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
526 lines
18 KiB
526 lines
18 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 1999 Werner Trobin <trobin@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
DESCRIPTION
|
|
|
|
When reading, the point of this module is toperform a depth-first traversal
|
|
of an OLE file. This ensures that a parent object is processed only after
|
|
its child objects have been processed.
|
|
*/
|
|
|
|
#include <olefilter.h>
|
|
|
|
#include <tqfile.h>
|
|
#include <tdetempfile.h>
|
|
#include <kgenericfactory.h>
|
|
#include <kmimetype.h>
|
|
#include <KoFilterChain.h>
|
|
#include <KoDocumentInfo.h>
|
|
|
|
//#include <excelfilter.h>
|
|
#include <powerpointfilter.h>
|
|
//#include <wordfilter.h>
|
|
//#include <hancomwordfilter.h>
|
|
|
|
#include <myfile.h>
|
|
|
|
const int OLEFilter::s_area = 30510;
|
|
|
|
class OLEFilterFactory : KGenericFactory<OLEFilter, KoFilter>
|
|
{
|
|
public:
|
|
OLEFilterFactory(void) : KGenericFactory<OLEFilter, KoFilter> ("olefilter")
|
|
{}
|
|
protected:
|
|
virtual void setupTranslations( void )
|
|
{
|
|
TDEGlobal::locale()->insertCatalogue( "kofficefilters" );
|
|
}
|
|
};
|
|
|
|
K_EXPORT_COMPONENT_FACTORY( libolefilter, OLEFilterFactory() )
|
|
|
|
OLEFilter::OLEFilter(KoFilter *, const char *, const TQStringList&) :
|
|
KoEmbeddingFilter(), numPic( 0 ),
|
|
docfile( 0 ), m_embeddeeData( 0 ),
|
|
m_embeddeeLength( 0 ), success( true )
|
|
{
|
|
olefile.data=0L;
|
|
}
|
|
|
|
OLEFilter::~OLEFilter()
|
|
{
|
|
delete [] olefile.data;
|
|
delete docfile;
|
|
}
|
|
|
|
KoFilter::ConversionStatus OLEFilter::convert( const TQCString& from, const TQCString& to )
|
|
{
|
|
if(to!="application/x-kword" &&
|
|
to!="application/x-kspread" &&
|
|
to!="application/x-kpresenter")
|
|
return KoFilter::NotImplemented;
|
|
if(from!="application/vnd.ms-word" &&
|
|
from!="application/vnd.ms-excel" &&
|
|
from!="application/msword" &&
|
|
from!="application/msexcel" &&
|
|
from!="application/mspowerpoint" &&
|
|
from!="application/x-hancomword")
|
|
return KoFilter::NotImplemented;
|
|
|
|
TQFile in(m_chain->inputFile());
|
|
if(!in.open(IO_ReadOnly)) {
|
|
kdError(s_area) << "OLEFilter::filter(): Unable to open input" << endl;
|
|
in.close();
|
|
return KoFilter::FileNotFound;
|
|
}
|
|
|
|
// Open the OLE 2 file. [TODO] Is it really the best way to
|
|
// read all the stuff without buffer?
|
|
olefile.length=in.size();
|
|
olefile.data=new unsigned char[olefile.length];
|
|
in.readBlock((char*)olefile.data, olefile.length);
|
|
in.close();
|
|
|
|
docfile=new KLaola(olefile);
|
|
if(!docfile->isOk()) {
|
|
kdError(s_area) << "OLEFilter::filter(): Unable to read input file correctly!" << endl;
|
|
delete [] olefile.data;
|
|
olefile.data=0L;
|
|
return KoFilter::StupidError;
|
|
}
|
|
|
|
// Recursively convert the file
|
|
convert( "" );
|
|
if ( success )
|
|
return KoFilter::OK;
|
|
else
|
|
return KoFilter::StupidError;
|
|
}
|
|
|
|
void OLEFilter::commSlotDelayStream( const char* delay )
|
|
{
|
|
emit internalCommDelayStream( delay );
|
|
}
|
|
|
|
void OLEFilter::commSlotShapeID( unsigned int& shapeID )
|
|
{
|
|
emit internalCommShapeID( shapeID );
|
|
}
|
|
|
|
void OLEFilter::slotSavePart(
|
|
const TQString &nameIN,
|
|
TQString &storageId,
|
|
TQString &mimeType,
|
|
const TQString &extension,
|
|
unsigned int length,
|
|
const char *data)
|
|
{
|
|
if(nameIN.isEmpty())
|
|
return;
|
|
|
|
int id = internalPartReference( nameIN );
|
|
|
|
if (id != -1)
|
|
{
|
|
// The part is already there, this is a lookup operation
|
|
// -> return the part id.
|
|
storageId = TQString::number( id );
|
|
mimeType = internalPartMimeType( nameIN );
|
|
}
|
|
else
|
|
{
|
|
// Set up the variables for the template method callback
|
|
m_embeddeeData = data;
|
|
m_embeddeeLength = length;
|
|
|
|
TQString srcMime( KoEmbeddingFilter::mimeTypeByExtension( extension ) );
|
|
if ( srcMime == KMimeType::defaultMimeType() )
|
|
kdWarning( s_area ) << "Couldn't determine the mimetype from the extension" << endl;
|
|
|
|
KoFilter::ConversionStatus status;
|
|
TQCString destMime( mimeType.latin1() );
|
|
storageId = TQString::number( embedPart( srcMime.latin1(), destMime, status, nameIN ) );
|
|
|
|
// copy back what the method returned
|
|
mimeType = destMime;
|
|
|
|
// Reset the variables to be on the safe side
|
|
m_embeddeeData = 0;
|
|
m_embeddeeLength = 0;
|
|
|
|
if ( status != KoFilter::OK )
|
|
kdDebug(s_area) << "Huh??? Couldn't convert that file" << endl;
|
|
}
|
|
}
|
|
|
|
void OLEFilter::slotSaveDocumentInformation(
|
|
const TQString &fullName,
|
|
const TQString &title,
|
|
const TQString &company,
|
|
const TQString &email,
|
|
const TQString &telephone,
|
|
const TQString &fax,
|
|
const TQString &postalCode,
|
|
const TQString &country,
|
|
const TQString &city,
|
|
const TQString &street,
|
|
const TQString &docTitle,
|
|
const TQString &docAbstract)
|
|
{
|
|
KoDocumentInfo *info = new KoDocumentInfo();
|
|
KoDocumentInfoAuthor *author = static_cast<KoDocumentInfoAuthor *>(info->page("author"));
|
|
KoDocumentInfoAbout *about = static_cast<KoDocumentInfoAbout *>(info->page("about"));
|
|
author->setFullName(fullName);
|
|
author->setTitle(title);
|
|
author->setCompany(company);
|
|
author->setEmail(email);
|
|
author->setTelephoneHome(telephone);
|
|
author->setFax(fax);
|
|
author->setCountry(postalCode);
|
|
author->setPostalCode(country);
|
|
author->setCity(city);
|
|
author->setStreet(street);
|
|
about->setTitle(docTitle);
|
|
about->setTitle(docAbstract);
|
|
|
|
KoStoreDevice* docInfo = m_chain->storageFile( "documentinfo.xml", KoStore::Write );
|
|
|
|
if(!docInfo)
|
|
{
|
|
kdError(s_area) << "OLEFilter::slotSaveDocumentInformation(): Could not open documentinfo.xml!" << endl;
|
|
return;
|
|
}
|
|
|
|
TQCString data = info->save().toCString();
|
|
// Important: don't use data.length() here. It's slow, and dangerous (in case of a '\0' somewhere)
|
|
// The -1 is because we don't want to write the final \0.
|
|
TQ_LONG length = data.size()-1;
|
|
|
|
if(docInfo->writeBlock(data, length) != length)
|
|
kdError(s_area) << "OLEFilter::slotSaveDocumentInformation(): Could not write to KoStore!" << endl;
|
|
}
|
|
|
|
void OLEFilter::slotSavePic(
|
|
const TQString &nameIN,
|
|
TQString &storageId,
|
|
const TQString &extension,
|
|
unsigned int length,
|
|
const char *data)
|
|
{
|
|
if(nameIN.isEmpty())
|
|
return;
|
|
|
|
TQMap<TQString, TQString>::ConstIterator it = imageMap.find(nameIN);
|
|
|
|
if (it != imageMap.end())
|
|
// The key is already here - return the part id.
|
|
storageId = it.data();
|
|
else
|
|
{
|
|
// It's not here, so let's generate one.
|
|
storageId = TQString( "pictures/picture%1.%2" ).arg( numPic++ ).arg( extension );
|
|
imageMap.insert(nameIN, storageId);
|
|
KoStoreDevice* pic = m_chain->storageFile( storageId, KoStore::Write );
|
|
if(!pic)
|
|
{
|
|
success = false;
|
|
kdError(s_area) << "OLEFilter::slotSavePic(): Could not open KoStore!" << endl;
|
|
return;
|
|
}
|
|
// Write it to the gzipped tar file
|
|
// Let's hope we never have to save images bigger than 2GB :-)
|
|
bool ret = pic->writeBlock(data, length) == static_cast<int>( length );
|
|
if (!ret)
|
|
kdError(s_area) << "OLEFilter::slotSavePic(): Could not write to KoStore!" << endl;
|
|
}
|
|
}
|
|
|
|
// ##### Only used for lookup now!
|
|
void OLEFilter::slotPart(
|
|
const TQString& nameIN,
|
|
TQString &storageId,
|
|
TQString &mimeType)
|
|
{
|
|
if (nameIN.isEmpty())
|
|
return;
|
|
|
|
int id = internalPartReference( nameIN );
|
|
|
|
if (id != -1)
|
|
{
|
|
// The key is already here - return the part id.
|
|
storageId = TQString::number( id );
|
|
mimeType = internalPartMimeType( nameIN );
|
|
}
|
|
else
|
|
kdWarning( s_area ) << "slotPart() can be used for lookup operations only" << endl;
|
|
}
|
|
|
|
// Don't forget the delete [] the stream.data ptr!
|
|
void OLEFilter::slotGetStream(const int &handle, myFile &stream) {
|
|
stream=docfile->stream(handle);
|
|
}
|
|
|
|
// I can't guarantee that you get the right stream as the names
|
|
// in a OLE 2 file are not unique! (searching only the current dir!)
|
|
// Don't forget the delete [] the stream.data ptr!
|
|
void OLEFilter::slotGetStream(const TQString &name, myFile &stream) {
|
|
|
|
KLaola::NodeList handle;
|
|
|
|
handle=docfile->find(name, true); // search only in current dir!
|
|
|
|
if (handle.count()==1)
|
|
stream=docfile->stream(handle.at(0));
|
|
else {
|
|
stream.data=0L;
|
|
stream.length=0;
|
|
}
|
|
}
|
|
|
|
void OLEFilter::savePartContents( TQIODevice* file )
|
|
{
|
|
if ( m_embeddeeData != 0 && m_embeddeeLength != 0 )
|
|
file->writeBlock( m_embeddeeData, m_embeddeeLength );
|
|
}
|
|
|
|
// The recursive method to do all the work
|
|
void OLEFilter::convert( const TQCString& mimeTypeHint )
|
|
{
|
|
KLaola::NodeList list=docfile->parseCurrentDir();
|
|
KLaola::OLENode *node;
|
|
bool onlyDirs=true;
|
|
|
|
// Search for the directories
|
|
for(node=list.first(); node!=0; node=list.next()) {
|
|
if(node->isDirectory()) { // It's a dir!
|
|
if(docfile->enterDir(node)) {
|
|
// Go one level deeper, but don't increase the depth
|
|
// for ObjectPools.
|
|
if (node->name() == "ObjectPool")
|
|
convert( "" );
|
|
else {
|
|
// Get the storage name of the part (dirname==key), and associate the
|
|
// mimeType with it for later use.
|
|
TQCString mimeHint( mimeTypeHelper() );
|
|
if ( mimeHint.isEmpty() )
|
|
mimeHint = "application/x-kword"; // will be converted to a dummy KWord part
|
|
startInternalEmbedding( node->name(), mimeHint );
|
|
convert( mimeHint );
|
|
endInternalEmbedding();
|
|
}
|
|
docfile->leaveDir();
|
|
}
|
|
}
|
|
else
|
|
onlyDirs=false; // To prevent useless looping in the next loop
|
|
}
|
|
|
|
if(!onlyDirs) {
|
|
TQStringList nodeNames;
|
|
TQCString mimeType;
|
|
if ( !mimeTypeHint.isEmpty() )
|
|
mimeType = mimeTypeHint;
|
|
else
|
|
mimeType = mimeTypeHelper();
|
|
|
|
FilterBase *myFilter=0L;
|
|
|
|
#if 0
|
|
if ( mimeType == "application/x-kword" ) {
|
|
// WinWord (or dummy).
|
|
|
|
myFile main;
|
|
KLaola::NodeList tmp;
|
|
tmp=docfile->find("WordDocument", true);
|
|
|
|
if(tmp.count()==1) {
|
|
// okay, not a dummy
|
|
main=docfile->stream(tmp.at(0));
|
|
|
|
myFile table0, table1, data;
|
|
tmp=docfile->find("0Table", true);
|
|
if(tmp.count()==1)
|
|
table0=docfile->stream(tmp.at(0));
|
|
|
|
tmp=docfile->find("1Table", true);
|
|
if(tmp.count()==1)
|
|
table1=docfile->stream(tmp.at(0));
|
|
|
|
tmp=docfile->find("Data", true);
|
|
if(tmp.count()==1)
|
|
data=docfile->stream(tmp.at(0));
|
|
|
|
myFilter=new WordFilter(main, table0, table1, data);
|
|
|
|
// forward the internal communication calls
|
|
connect( this, TQT_SIGNAL( internalCommShapeID( unsigned int& ) ), myFilter, TQT_SIGNAL( internalCommShapeID( unsigned int& ) ) );
|
|
connect( this, TQT_SIGNAL( internalCommDelayStream( const char* ) ), myFilter, TQT_SIGNAL( internalCommDelayStream( const char* ) ) );
|
|
}
|
|
}
|
|
else if ( mimeType == "application/x-kspread" ) {
|
|
// Excel.
|
|
|
|
myFile workbook;
|
|
KLaola::NodeList tmp;
|
|
|
|
tmp = docfile->find( "Workbook", true );
|
|
if ( tmp.count() == 1 )
|
|
workbook = docfile->stream( tmp.at( 0 ) );
|
|
else {
|
|
tmp = docfile->find( "Book", true );
|
|
if ( tmp.count() == 1 )
|
|
workbook = docfile->stream( tmp.at( 0 ) );
|
|
}
|
|
myFilter=new ExcelFilter(workbook);
|
|
}
|
|
else
|
|
#endif
|
|
if ( mimeType == "application/x-kpresenter" ) {
|
|
// Powerpoint.
|
|
|
|
myFile main, currentUser, pictures, summary, documentSummary;
|
|
KLaola::NodeList tmp;
|
|
|
|
tmp=docfile->find("PowerPoint Document", true);
|
|
if(tmp.count()==1)
|
|
main=docfile->stream(tmp.at(0));
|
|
|
|
tmp=docfile->find("Current User", true);
|
|
if(tmp.count()==1)
|
|
currentUser=docfile->stream(tmp.at(0));
|
|
|
|
tmp=docfile->find("Pictures", true);
|
|
if(tmp.count()==1)
|
|
pictures=docfile->stream(tmp.at(0));
|
|
|
|
tmp=docfile->find("SummaryInformation", true);
|
|
if(tmp.count()==1)
|
|
summary=docfile->stream(tmp.at(0));
|
|
|
|
tmp=docfile->find("DocumentSummaryInformation", true);
|
|
if(tmp.count()==1)
|
|
documentSummary=docfile->stream(tmp.at(0));
|
|
|
|
myFilter=new PowerPointFilter(main, currentUser, pictures);
|
|
}
|
|
#if 0
|
|
else if ( mimeType == "application/x-hancomword" ) {
|
|
// HancomWord 6
|
|
myFile prvText;
|
|
KLaola::NodeList tmp;
|
|
|
|
tmp = docfile->find( "PrvText", true );
|
|
if( tmp.count() == 1 ) prvText = docfile->stream( tmp.at( 0 ) );
|
|
|
|
myFilter = new HancomWordFilter( prvText );
|
|
}
|
|
#endif
|
|
|
|
if(!myFilter) {
|
|
// Unknown type. We turn it into a dummy kword document...
|
|
node = list.first();
|
|
do {
|
|
nodeNames.prepend(node->name());
|
|
node = list.next();
|
|
} while ( node );
|
|
|
|
kdWarning(s_area) << "cannot convert \"" << nodeNames.join(",") << "\"" << endl;
|
|
myFilter=new FilterBase(nodeNames);
|
|
}
|
|
|
|
// connect SIGNALs&SLOTs
|
|
connectCommon(&myFilter);
|
|
|
|
// Launch the filtering process...
|
|
success=myFilter->filter();
|
|
// ...and fetch the file
|
|
TQCString file;
|
|
if(!myFilter->plainString()) {
|
|
const TQDomDocument * const part=myFilter->part();
|
|
file=part->toCString();
|
|
}
|
|
else
|
|
file=myFilter->CString();
|
|
|
|
KoStoreDevice* dev = m_chain->storageFile( "root", KoStore::Write );
|
|
if(!dev) {
|
|
success=false;
|
|
kdError(s_area) << "OLEFilter::convert(): Could not open KoStore!" << endl;
|
|
return;
|
|
}
|
|
|
|
// Write it to the gzipped tar file
|
|
bool ret = dev->writeBlock(file.data(), file.size()-1) == static_cast<TQ_LONG>( file.size() - 1 );
|
|
if (!ret)
|
|
kdError(s_area) << "OLEFilter::slotSavePic(): Could not write to KoStore!" << endl;
|
|
delete myFilter;
|
|
}
|
|
}
|
|
|
|
void OLEFilter::connectCommon(FilterBase **myFilter) {
|
|
TQObject::connect(
|
|
*myFilter,
|
|
TQT_SIGNAL(signalSaveDocumentInformation(const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &)),
|
|
this,
|
|
TQT_SLOT(slotSaveDocumentInformation(const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &, const TQString &)));
|
|
|
|
TQObject::connect(
|
|
*myFilter,
|
|
TQT_SIGNAL(signalSavePic(const TQString &, TQString &, const TQString &, unsigned int, const char *)),
|
|
this,
|
|
TQT_SLOT(slotSavePic(const TQString &, TQString &, const TQString &, unsigned int, const char *)));
|
|
TQObject::connect(
|
|
*myFilter,
|
|
TQT_SIGNAL(signalSavePart(const TQString &, TQString &, TQString &, const TQString &, unsigned int, const char *)),
|
|
this,
|
|
TQT_SLOT(slotSavePart(const TQString &, TQString &, TQString &, const TQString &, unsigned int, const char *)));
|
|
TQObject::connect(*myFilter, TQT_SIGNAL(signalPart(const TQString&, TQString &, TQString &)),
|
|
this, TQT_SLOT(slotPart(const TQString&, TQString &, TQString &)));
|
|
TQObject::connect(*myFilter, TQT_SIGNAL(signalGetStream(const int &, myFile &)), this,
|
|
TQT_SLOT(slotGetStream(const int &, myFile &)));
|
|
TQObject::connect(*myFilter, TQT_SIGNAL(signalGetStream(const TQString &, myFile &)), this,
|
|
TQT_SLOT(slotGetStream(const TQString &, myFile &)));
|
|
TQObject::connect(*myFilter, TQT_SIGNAL(sigProgress(int)), this, TQT_SIGNAL(sigProgress(int)));
|
|
}
|
|
|
|
TQCString OLEFilter::mimeTypeHelper()
|
|
{
|
|
KLaola::NodeList list = docfile->parseCurrentDir();
|
|
KLaola::OLENode* node = list.first();
|
|
|
|
// ###### FIXME: Shaheed, please add additional mimetypes here
|
|
while ( node ) {
|
|
if ( node->name() == "WordDocument" )
|
|
return "application/x-kword";
|
|
else if ( node->name() == "Workbook" || node->name() == "Book" )
|
|
return "application/x-kspread";
|
|
else if ( node->name() == "PowerPoint Document" )
|
|
return "application/x-kpresenter";
|
|
else if ( node->name() == "PrvText" || node->name() == "BodyText" )
|
|
return "application/x-hancomword";
|
|
else
|
|
node = list.next();
|
|
}
|
|
kdWarning( s_area ) << "No known mimetype detected" << endl;
|
|
return "";
|
|
}
|
|
|
|
#include <olefilter.moc>
|