filters

olefilter.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 1999 Werner Trobin <trobin@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017    Boston, MA 02111-1307, USA.
00018 
00019 DESCRIPTION
00020 
00021    When reading, the point of this module is toperform a depth-first traversal
00022    of an OLE file. This ensures that a parent object is processed only after
00023    its child objects have been processed.
00024 */
00025 
00026 #include <olefilter.h>
00027 
00028 #include <qfile.h>
00029 #include <ktempfile.h>
00030 #include <kgenericfactory.h>
00031 #include <kmimetype.h>
00032 #include <KoFilterChain.h>
00033 #include <KoDocumentInfo.h>
00034 
00035 //#include <excelfilter.h>
00036 #include <powerpointfilter.h>
00037 //#include <wordfilter.h>
00038 //#include <hancomwordfilter.h>
00039 
00040 #include <myfile.h>
00041 
00042 const int OLEFilter::s_area = 30510;
00043 
00044 class OLEFilterFactory : KGenericFactory<OLEFilter, KoFilter>
00045 {
00046 public:
00047     OLEFilterFactory(void) : KGenericFactory<OLEFilter, KoFilter> ("olefilter")
00048     {}
00049 protected:
00050     virtual void setupTranslations( void )
00051     {
00052         KGlobal::locale()->insertCatalogue( "kofficefilters" );
00053     }
00054 };
00055 
00056 K_EXPORT_COMPONENT_FACTORY( libolefilter, OLEFilterFactory() )
00057 
00058 OLEFilter::OLEFilter(KoFilter *, const char *, const QStringList&) :
00059                      KoEmbeddingFilter(), numPic( 0 ),
00060                      docfile( 0 ), m_embeddeeData( 0 ),
00061                      m_embeddeeLength( 0 ), success( true )
00062 {
00063     olefile.data=0L;
00064 }
00065 
00066 OLEFilter::~OLEFilter()
00067 {
00068     delete [] olefile.data;
00069     delete docfile;
00070 }
00071 
00072 KoFilter::ConversionStatus OLEFilter::convert( const QCString& from, const QCString& to )
00073 {
00074     if(to!="application/x-kword" &&
00075        to!="application/x-kspread" &&
00076        to!="application/x-kpresenter")
00077         return KoFilter::NotImplemented;
00078     if(from!="application/vnd.ms-word" &&
00079        from!="application/vnd.ms-excel" &&
00080        from!="application/msword" &&
00081        from!="application/msexcel" &&
00082        from!="application/mspowerpoint" &&
00083        from!="application/x-hancomword")
00084         return KoFilter::NotImplemented;
00085 
00086     QFile in(m_chain->inputFile());
00087     if(!in.open(IO_ReadOnly)) {
00088         kdError(s_area) << "OLEFilter::filter(): Unable to open input" << endl;
00089         in.close();
00090         return KoFilter::FileNotFound;
00091     }
00092 
00093     // Open the OLE 2 file. [TODO] Is it really the best way to
00094     // read all the stuff without buffer?
00095     olefile.length=in.size();
00096     olefile.data=new unsigned char[olefile.length];
00097     in.readBlock((char*)olefile.data, olefile.length);
00098     in.close();
00099 
00100     docfile=new KLaola(olefile);
00101     if(!docfile->isOk()) {
00102         kdError(s_area) << "OLEFilter::filter(): Unable to read input file correctly!" << endl;
00103         delete [] olefile.data;
00104         olefile.data=0L;
00105         return KoFilter::StupidError;
00106     }
00107 
00108     // Recursively convert the file
00109     convert( "" );
00110     if ( success )
00111         return KoFilter::OK;
00112     else
00113         return KoFilter::StupidError;
00114 }
00115 
00116 void OLEFilter::commSlotDelayStream( const char* delay )
00117 {
00118     emit internalCommDelayStream( delay );
00119 }
00120 
00121 void OLEFilter::commSlotShapeID( unsigned int& shapeID )
00122 {
00123     emit internalCommShapeID( shapeID );
00124 }
00125 
00126 void OLEFilter::slotSavePart(
00127     const QString &nameIN,
00128     QString &storageId,
00129     QString &mimeType,
00130     const QString &extension,
00131     unsigned int length,
00132     const char *data)
00133 {
00134     if(nameIN.isEmpty())
00135         return;
00136 
00137     int id = internalPartReference( nameIN );
00138 
00139     if (id != -1)
00140     {
00141         // The part is already there, this is a lookup operation
00142         // -> return the part id.
00143         storageId = QString::number( id );
00144         mimeType = internalPartMimeType( nameIN );
00145     }
00146     else
00147     {
00148         // Set up the variables for the template method callback
00149         m_embeddeeData = data;
00150         m_embeddeeLength = length;
00151 
00152         QString srcMime( KoEmbeddingFilter::mimeTypeByExtension( extension ) );
00153         if ( srcMime == KMimeType::defaultMimeType() )
00154             kdWarning( s_area ) << "Couldn't determine the mimetype from the extension" << endl;
00155 
00156         KoFilter::ConversionStatus status;
00157         QCString destMime( mimeType.latin1() );
00158         storageId = QString::number( embedPart( srcMime.latin1(), destMime, status, nameIN ) );
00159 
00160         // copy back what the method returned
00161         mimeType = destMime;
00162 
00163         // Reset the variables to be on the safe side
00164         m_embeddeeData = 0;
00165         m_embeddeeLength = 0;
00166 
00167         if ( status != KoFilter::OK )
00168             kdDebug(s_area) << "Huh??? Couldn't convert that file" << endl;
00169     }
00170 }
00171 
00172 void OLEFilter::slotSaveDocumentInformation(
00173     const QString &fullName,
00174     const QString &title,
00175     const QString &company,
00176     const QString &email,
00177     const QString &telephone,
00178     const QString &fax,
00179     const QString &postalCode,
00180     const QString &country,
00181     const QString &city,
00182     const QString &street,
00183     const QString &docTitle,
00184     const QString &docAbstract)
00185 {
00186     KoDocumentInfo *info = new KoDocumentInfo();
00187     KoDocumentInfoAuthor *author = static_cast<KoDocumentInfoAuthor *>(info->page("author"));
00188     KoDocumentInfoAbout *about = static_cast<KoDocumentInfoAbout *>(info->page("about"));
00189     author->setFullName(fullName);
00190     author->setTitle(title);
00191     author->setCompany(company);
00192     author->setEmail(email);
00193     author->setTelephoneHome(telephone);
00194     author->setFax(fax);
00195     author->setCountry(postalCode);
00196     author->setPostalCode(country);
00197     author->setCity(city);
00198     author->setStreet(street);
00199     about->setTitle(docTitle);
00200     about->setTitle(docAbstract);
00201 
00202     KoStoreDevice* docInfo = m_chain->storageFile( "documentinfo.xml", KoStore::Write );
00203 
00204     if(!docInfo)
00205     {
00206     kdError(s_area) << "OLEFilter::slotSaveDocumentInformation(): Could not open documentinfo.xml!" << endl;
00207         return;
00208     }
00209 
00210     QCString data = info->save().toCString();
00211     // Important: don't use data.length() here. It's slow, and dangerous (in case of a '\0' somewhere)
00212     // The -1 is because we don't want to write the final \0.
00213     Q_LONG length = data.size()-1;
00214 
00215     if(docInfo->writeBlock(data, length) != length)
00216     kdError(s_area) << "OLEFilter::slotSaveDocumentInformation(): Could not write to KoStore!" << endl;
00217 }
00218 
00219 void OLEFilter::slotSavePic(
00220     const QString &nameIN,
00221     QString &storageId,
00222     const QString &extension,
00223     unsigned int length,
00224     const char *data)
00225 {
00226     if(nameIN.isEmpty())
00227         return;
00228 
00229     QMap<QString, QString>::ConstIterator it = imageMap.find(nameIN);
00230 
00231     if (it != imageMap.end())
00232         // The key is already here - return the part id.
00233         storageId = it.data();
00234     else
00235     {
00236         // It's not here, so let's generate one.
00237         storageId = QString( "pictures/picture%1.%2" ).arg( numPic++ ).arg( extension );
00238         imageMap.insert(nameIN, storageId);
00239         KoStoreDevice* pic = m_chain->storageFile( storageId, KoStore::Write );
00240         if(!pic)
00241         {
00242             success = false;
00243             kdError(s_area) << "OLEFilter::slotSavePic(): Could not open KoStore!" << endl;
00244             return;
00245         }
00246         // Write it to the gzipped tar file
00247         // Let's hope we never have to save images bigger than 2GB :-)
00248         bool ret = pic->writeBlock(data, length) == static_cast<int>( length );
00249         if (!ret)
00250             kdError(s_area) << "OLEFilter::slotSavePic(): Could not write to KoStore!" << endl;
00251     }
00252 }
00253 
00254 // ##### Only used for lookup now!
00255 void OLEFilter::slotPart(
00256     const QString& nameIN,
00257     QString &storageId,
00258     QString &mimeType)
00259 {
00260     if (nameIN.isEmpty())
00261         return;
00262 
00263     int id = internalPartReference( nameIN );
00264 
00265     if (id != -1)
00266     {
00267         // The key is already here - return the part id.
00268         storageId = QString::number( id );
00269         mimeType = internalPartMimeType( nameIN );
00270     }
00271     else
00272         kdWarning( s_area ) << "slotPart() can be used for lookup operations only" << endl;
00273 }
00274 
00275 // Don't forget the delete [] the stream.data ptr!
00276 void OLEFilter::slotGetStream(const int &handle, myFile &stream) {
00277     stream=docfile->stream(handle);
00278 }
00279 
00280 // I can't guarantee that you get the right stream as the names
00281 // in a OLE 2 file are not unique! (searching only the current dir!)
00282 // Don't forget the delete [] the stream.data ptr!
00283 void OLEFilter::slotGetStream(const QString &name, myFile &stream) {
00284 
00285     KLaola::NodeList handle;
00286 
00287     handle=docfile->find(name, true);  // search only in current dir!
00288 
00289     if (handle.count()==1)
00290         stream=docfile->stream(handle.at(0));
00291     else {
00292         stream.data=0L;
00293         stream.length=0;
00294     }
00295 }
00296 
00297 void OLEFilter::savePartContents( QIODevice* file )
00298 {
00299     if ( m_embeddeeData != 0 && m_embeddeeLength != 0 )
00300         file->writeBlock( m_embeddeeData, m_embeddeeLength );
00301 }
00302 
00303 // The recursive method to do all the work
00304 void OLEFilter::convert( const QCString& mimeTypeHint )
00305 {
00306     KLaola::NodeList list=docfile->parseCurrentDir();
00307     KLaola::OLENode *node;
00308     bool onlyDirs=true;
00309 
00310     // Search for the directories
00311     for(node=list.first(); node!=0; node=list.next()) {
00312         if(node->isDirectory()) {   // It's a dir!
00313             if(docfile->enterDir(node)) {
00314                 // Go one level deeper, but don't increase the depth
00315                 // for ObjectPools.
00316                 if (node->name() == "ObjectPool")
00317                     convert( "" );
00318                 else {
00319                     // Get the storage name of the part (dirname==key), and associate the
00320                     // mimeType with it for later use.
00321                     QCString mimeHint( mimeTypeHelper() );
00322                     if ( mimeHint.isEmpty() )
00323                         mimeHint = "application/x-kword"; // will be converted to a dummy KWord part
00324                     startInternalEmbedding( node->name(), mimeHint );
00325                     convert( mimeHint );
00326                     endInternalEmbedding();
00327                 }
00328                 docfile->leaveDir();
00329             }
00330         }
00331         else
00332             onlyDirs=false;   // To prevent useless looping in the next loop
00333     }
00334 
00335     if(!onlyDirs) {
00336         QStringList nodeNames;
00337         QCString mimeType;
00338         if ( !mimeTypeHint.isEmpty() )
00339             mimeType = mimeTypeHint;
00340         else
00341             mimeType = mimeTypeHelper();
00342 
00343         FilterBase *myFilter=0L;
00344 
00345     #if 0
00346         if ( mimeType == "application/x-kword" ) {
00347             // WinWord (or dummy).
00348 
00349             myFile main;
00350             KLaola::NodeList tmp;
00351             tmp=docfile->find("WordDocument", true);
00352 
00353             if(tmp.count()==1) {
00354                 // okay, not a dummy
00355                 main=docfile->stream(tmp.at(0));
00356 
00357                 myFile table0, table1, data;
00358                 tmp=docfile->find("0Table", true);
00359                 if(tmp.count()==1)
00360                     table0=docfile->stream(tmp.at(0));
00361 
00362                 tmp=docfile->find("1Table", true);
00363                 if(tmp.count()==1)
00364                     table1=docfile->stream(tmp.at(0));
00365 
00366                 tmp=docfile->find("Data", true);
00367                 if(tmp.count()==1)
00368                     data=docfile->stream(tmp.at(0));
00369 
00370                 myFilter=new WordFilter(main, table0, table1, data);
00371 
00372                 // forward the internal communication calls
00373                 connect( this, SIGNAL( internalCommShapeID( unsigned int& ) ), myFilter, SIGNAL( internalCommShapeID( unsigned int& ) ) );
00374                 connect( this, SIGNAL( internalCommDelayStream( const char* ) ), myFilter, SIGNAL( internalCommDelayStream( const char* ) ) );
00375             }
00376         }
00377         else if ( mimeType == "application/x-kspread" ) {
00378             // Excel.
00379 
00380             myFile workbook;
00381             KLaola::NodeList tmp;
00382 
00383             tmp = docfile->find( "Workbook", true );
00384             if ( tmp.count() == 1 )
00385                 workbook = docfile->stream( tmp.at( 0 ) );
00386             else {
00387                 tmp = docfile->find( "Book", true );
00388                 if ( tmp.count() == 1 )
00389                     workbook = docfile->stream( tmp.at( 0 ) );
00390             }
00391             myFilter=new ExcelFilter(workbook);
00392         }
00393         else
00394     #endif       
00395     if ( mimeType == "application/x-kpresenter" ) {
00396             // Powerpoint.
00397 
00398             myFile main, currentUser, pictures, summary, documentSummary;
00399             KLaola::NodeList tmp;
00400 
00401             tmp=docfile->find("PowerPoint Document", true);
00402             if(tmp.count()==1)
00403                 main=docfile->stream(tmp.at(0));
00404 
00405             tmp=docfile->find("Current User", true);
00406             if(tmp.count()==1)
00407                 currentUser=docfile->stream(tmp.at(0));
00408 
00409             tmp=docfile->find("Pictures", true);
00410             if(tmp.count()==1)
00411                 pictures=docfile->stream(tmp.at(0));
00412 
00413             tmp=docfile->find("SummaryInformation", true);
00414             if(tmp.count()==1)
00415                 summary=docfile->stream(tmp.at(0));
00416 
00417             tmp=docfile->find("DocumentSummaryInformation", true);
00418             if(tmp.count()==1)
00419                 documentSummary=docfile->stream(tmp.at(0));
00420 
00421             myFilter=new PowerPointFilter(main, currentUser, pictures);
00422         }
00423     #if 0
00424         else if ( mimeType == "application/x-hancomword" ) {
00425             // HancomWord 6
00426             myFile prvText;
00427             KLaola::NodeList tmp;
00428 
00429             tmp = docfile->find( "PrvText", true );
00430             if( tmp.count() == 1 ) prvText = docfile->stream( tmp.at( 0 ) );
00431 
00432             myFilter = new HancomWordFilter( prvText );
00433         }
00434     #endif
00435 
00436         if(!myFilter) {
00437             // Unknown type. We turn it into a dummy kword document...
00438             node = list.first();
00439             do {
00440                 nodeNames.prepend(node->name());
00441                 node = list.next();
00442             } while ( node );
00443 
00444             kdWarning(s_area) << "cannot convert \"" << nodeNames.join(",") << "\"" << endl;
00445             myFilter=new FilterBase(nodeNames);
00446         }
00447 
00448         // connect SIGNALs&SLOTs
00449         connectCommon(&myFilter);
00450 
00451         // Launch the filtering process...
00452         success=myFilter->filter();
00453         // ...and fetch the file
00454         QCString file;
00455         if(!myFilter->plainString()) {
00456             const QDomDocument * const part=myFilter->part();
00457             file=part->toCString();
00458         }
00459         else
00460             file=myFilter->CString();
00461 
00462         KoStoreDevice* dev = m_chain->storageFile( "root", KoStore::Write );
00463         if(!dev) {
00464             success=false;
00465             kdError(s_area) << "OLEFilter::convert(): Could not open KoStore!" << endl;
00466             return;
00467         }
00468 
00469         // Write it to the gzipped tar file
00470         bool ret = dev->writeBlock(file.data(), file.size()-1) == static_cast<Q_LONG>( file.size() - 1 );
00471         if (!ret)
00472             kdError(s_area) << "OLEFilter::slotSavePic(): Could not write to KoStore!" << endl;
00473         delete myFilter;
00474     }
00475 }
00476 
00477 void OLEFilter::connectCommon(FilterBase **myFilter) {
00478     QObject::connect(
00479         *myFilter,
00480         SIGNAL(signalSaveDocumentInformation(const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &)),
00481         this,
00482         SLOT(slotSaveDocumentInformation(const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &, const QString &)));
00483 
00484     QObject::connect(
00485         *myFilter,
00486         SIGNAL(signalSavePic(const QString &, QString &, const QString &, unsigned int, const char *)),
00487         this,
00488         SLOT(slotSavePic(const QString &, QString &, const QString &, unsigned int, const char *)));
00489     QObject::connect(
00490         *myFilter,
00491         SIGNAL(signalSavePart(const QString &, QString &, QString &, const QString &, unsigned int, const char *)),
00492         this,
00493         SLOT(slotSavePart(const QString &, QString &, QString &, const QString &, unsigned int, const char *)));
00494     QObject::connect(*myFilter, SIGNAL(signalPart(const QString&, QString &, QString &)),
00495                      this, SLOT(slotPart(const QString&, QString &, QString &)));
00496     QObject::connect(*myFilter, SIGNAL(signalGetStream(const int &, myFile &)), this,
00497                      SLOT(slotGetStream(const int &, myFile &)));
00498     QObject::connect(*myFilter, SIGNAL(signalGetStream(const QString &, myFile &)), this,
00499                      SLOT(slotGetStream(const QString &, myFile &)));
00500     QObject::connect(*myFilter, SIGNAL(sigProgress(int)), this, SIGNAL(sigProgress(int)));
00501 }
00502 
00503 QCString OLEFilter::mimeTypeHelper()
00504 {
00505     KLaola::NodeList list = docfile->parseCurrentDir();
00506     KLaola::OLENode* node = list.first();
00507 
00508     // ###### FIXME: Shaheed, please add additional mimetypes here
00509     while ( node ) {
00510         if ( node->name() == "WordDocument" )
00511             return "application/x-kword";
00512         else if ( node->name() == "Workbook" || node->name() == "Book" )
00513             return "application/x-kspread";
00514         else if ( node->name() == "PowerPoint Document" )
00515             return "application/x-kpresenter";
00516         else if ( node->name() == "PrvText" || node->name() == "BodyText" )
00517             return "application/x-hancomword";
00518         else
00519             node = list.next();
00520     }
00521     kdWarning( s_area ) << "No known mimetype detected" << endl;
00522     return "";
00523 }
00524 
00525 #include <olefilter.moc>
KDE Home | KDE Accessibility Home | Description of Access Keys