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.
koffice/filters/olefilters/olefilter.cc

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 <ktempfile.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>