Add API to load and save "document archives", ie an .okular archive with document and metadata (currently annotations).

Few missing to be done in it:
- more checks when saving
- make it use a proper mimetype

CCBUG: 151614

svn path=/trunk/KDE/kdegraphics/okular/; revision=884597
This commit is contained in:
Pino Toscano 2008-11-15 14:15:31 +00:00
parent cd6f369fc1
commit b6c5ba1bc8
3 changed files with 220 additions and 2 deletions

View file

@ -45,6 +45,7 @@
#include <kstandarddirs.h>
#include <ktemporaryfile.h>
#include <ktoolinvocation.h>
#include <kzip.h>
// local includes
#include "action.h"
@ -69,9 +70,12 @@
#include "sourcereference.h"
#include "sourcereference_p.h"
#include "texteditors_p.h"
#include "utils_p.h"
#include "view.h"
#include "view_p.h"
#include <memory>
#include <config-okular.h>
using namespace Okular;
@ -86,6 +90,16 @@ struct AllocatedPixmap
AllocatedPixmap( int i, int p, qulonglong m ) : id( i ), page( p ), memory( m ) {}
};
struct ArchiveData
{
ArchiveData()
{
}
KTemporaryFile document;
QString metadataFileName;
};
struct RunningSearch
{
// store search properties
@ -671,6 +685,36 @@ bool DocumentPrivate::openDocumentInternal( const KService::Ptr& offer, bool iss
return openOk;
}
bool DocumentPrivate::savePageDocumentInfo( KTemporaryFile *infoFile, int what ) const
{
if ( infoFile->open() )
{
// 1. Create DOM
QDomDocument doc( "documentInfo" );
QDomProcessingInstruction xmlPi = doc.createProcessingInstruction(
QString::fromLatin1( "xml" ), QString::fromLatin1( "version=\"1.0\" encoding=\"utf-8\"" ) );
doc.appendChild( xmlPi );
QDomElement root = doc.createElement( "documentInfo" );
doc.appendChild( root );
// 2.1. Save page attributes (bookmark state, annotations, ... ) to DOM
QDomElement pageList = doc.createElement( "pageList" );
root.appendChild( pageList );
// <page list><page number='x'>.... </page> save pages that hold data
QVector< Page * >::const_iterator pIt = m_pagesVector.constBegin(), pEnd = m_pagesVector.constEnd();
for ( ; pIt != pEnd; ++pIt )
(*pIt)->d->saveLocalContents( pageList, doc, PageItems( what ) );
// 3. Save DOM to XML file
QString xml = doc.toString();
QTextStream os( infoFile );
os.setCodec( "UTF-8" );
os << xml;
return true;
}
return false;
}
void DocumentPrivate::saveDocumentInfo() const
{
if ( m_xmlFileName.isEmpty() )
@ -1461,7 +1505,7 @@ bool Document::openDocument( const QString & docFile, const KUrl& url, const KMi
// determine the related "xml document-info" filename
d->m_url = url;
d->m_docFileName = docFile;
if ( url.isLocalFile() )
if ( url.isLocalFile() && !d->m_archiveData )
{
QString fn = url.fileName();
document_size = fileReadTest.size();
@ -1566,7 +1610,14 @@ bool Document::openDocument( const QString & docFile, const KUrl& url, const KMi
d->m_generatorName = offer->name();
// 2. load Additional Data (our bookmarks and metadata) about the document
d->loadDocumentInfo();
if ( d->m_archiveData )
{
d->loadDocumentInfo( d->m_archiveData->metadataFileName );
}
else
{
d->loadDocumentInfo();
}
d->m_bookmarkManager->setUrl( d->m_url );
// 3. setup observers inernal lists and data
@ -1702,6 +1753,8 @@ void Document::closeDocument()
d->m_xmlFileName = QString();
delete d->m_tempFile;
d->m_tempFile = 0;
delete d->m_archiveData;
d->m_archiveData = 0;
d->m_docSize = -1;
d->m_exportCached = false;
d->m_exportFormats.clear();
@ -3090,6 +3143,152 @@ QByteArray Document::fontData(const FontInfo &font) const
return result;
}
bool Document::openDocumentArchive( const QString & docFile, const KUrl & url )
{
const KMimeType::Ptr mime = KMimeType::findByPath( docFile, 0, false /* content too */ );
if ( !mime->is( "application/zip" ) ) // ### use correct mimetype
return false;
KZip okularArchive( docFile );
if ( !okularArchive.open( QIODevice::ReadOnly ) )
return false;
const KArchiveDirectory * mainDir = okularArchive.directory();
const KArchiveEntry * mainEntry = mainDir->entry( "content.xml" );
if ( !mainEntry || !mainEntry->isFile() )
return false;
std::auto_ptr< QIODevice > mainEntryDevice( static_cast< const KZipFileEntry * >( mainEntry )->createDevice() );
QDomDocument doc;
if ( !doc.setContent( mainEntryDevice.get() ) )
return false;
mainEntryDevice.reset();
QDomElement root = doc.documentElement();
if ( root.tagName() != "OkularArchive" )
return false;
QString documentFileName;
QString metadataFileName;
QDomElement el = root.firstChild().toElement();
for ( ; !el.isNull(); el = el.nextSibling().toElement() )
{
if ( el.tagName() == "Files" )
{
QDomElement fileEl = el.firstChild().toElement();
for ( ; !fileEl.isNull(); fileEl = fileEl.nextSibling().toElement() )
{
if ( fileEl.tagName() == "DocumentFileName" )
documentFileName = fileEl.text();
else if ( fileEl.tagName() == "MetadataFileName" )
metadataFileName = fileEl.text();
}
}
}
if ( documentFileName.isEmpty() )
return false;
const KArchiveEntry * docEntry = mainDir->entry( documentFileName );
if ( !docEntry || !docEntry->isFile() )
return false;
std::auto_ptr< ArchiveData > archiveData( new ArchiveData() );
const int dotPos = documentFileName.indexOf( '.' );
if ( dotPos != -1 )
archiveData->document.setSuffix( documentFileName.mid( dotPos ) );
if ( !archiveData->document.open() )
return false;
QString tempFileName = archiveData->document.fileName();
{
std::auto_ptr< QIODevice > docEntryDevice( static_cast< const KZipFileEntry * >( docEntry )->createDevice() );
copyQIODevice( docEntryDevice.get(), &archiveData->document );
archiveData->document.close();
}
std::auto_ptr< KTemporaryFile > tempMetadataFileName;
const KArchiveEntry * metadataEntry = mainDir->entry( metadataFileName );
if ( metadataEntry && metadataEntry->isFile() )
{
std::auto_ptr< QIODevice > metadataEntryDevice( static_cast< const KZipFileEntry * >( metadataEntry )->createDevice() );
tempMetadataFileName.reset( new KTemporaryFile() );
tempMetadataFileName->setSuffix( ".xml" );
tempMetadataFileName->setAutoRemove( false );
if ( tempMetadataFileName->open() )
{
copyQIODevice( metadataEntryDevice.get(), tempMetadataFileName.get() );
archiveData->metadataFileName = tempMetadataFileName->fileName();
tempMetadataFileName->close();
}
}
const KMimeType::Ptr docMime = KMimeType::findByPath( tempFileName, 0, true /* local file */ );
d->m_archiveData = archiveData.get();
bool ret = openDocument( tempFileName, url, docMime );
if ( ret )
{
archiveData.release();
}
else
{
d->m_archiveData = 0;
}
return ret;
}
bool Document::saveDocumentArchive( const QString &fileName )
{
if ( !d->m_generator )
return false;
QString docFileName = d->m_url.fileName();
if ( docFileName == QLatin1String( "-" ) )
return false;
KZip okularArchive( fileName );
if ( !okularArchive.open( QIODevice::WriteOnly ) )
return false;
const KUser user;
const KUserGroup userGroup( user.gid() );
QDomDocument contentDoc( "OkularArchive" );
QDomProcessingInstruction xmlPi = contentDoc.createProcessingInstruction(
QString::fromLatin1( "xml" ), QString::fromLatin1( "version=\"1.0\" encoding=\"utf-8\"" ) );
contentDoc.appendChild( xmlPi );
QDomElement root = contentDoc.createElement( "OkularArchive" );
contentDoc.appendChild( root );
QDomElement filesNode = contentDoc.createElement( "Files" );
root.appendChild( filesNode );
QDomElement fileNameNode = contentDoc.createElement( "DocumentFileName" );
filesNode.appendChild( fileNameNode );
fileNameNode.appendChild( contentDoc.createTextNode( docFileName ) );
QDomElement metadataFileNameNode = contentDoc.createElement( "MetadataFileName" );
filesNode.appendChild( metadataFileNameNode );
metadataFileNameNode.appendChild( contentDoc.createTextNode( "metadata.xml" ) );
KTemporaryFile metadataFile;
if ( !d->savePageDocumentInfo( &metadataFile, AnnotationPageItems ) )
return false;
const QByteArray contentDocXml = contentDoc.toByteArray();
okularArchive.writeFile( "content.xml", user.loginName(), userGroup.name(),
contentDocXml.constData(), contentDocXml.length() );
okularArchive.addLocalFile( d->m_docFileName, docFileName );
okularArchive.addLocalFile( metadataFile.fileName(), "metadata.xml" );
if ( !okularArchive.close() )
return false;
return true;
}
void DocumentPrivate::requestDone( PixmapRequest * req )
{
if ( !req )

View file

@ -558,6 +558,20 @@ class OKULAR_EXPORT Document : public QObject
*/
QByteArray fontData(const FontInfo &font) const;
/**
* Opens a document archive.
*
* @since 0.8 (KDE 4.2)
*/
bool openDocumentArchive( const QString & docFile, const KUrl & url );
/**
* Saves a document archive.
*
* @since 0.8 (KDE 4.2)
*/
bool saveDocumentArchive( const QString &fileName );
public Q_SLOTS:
/**
* This slot is called whenever the user changes the @p rotation of

View file

@ -32,6 +32,7 @@ class QTimer;
class KTemporaryFile;
struct AllocatedPixmap;
struct ArchiveData;
struct RunningSearch;
namespace Okular {
@ -82,6 +83,7 @@ class DocumentPrivate
m_generatorsLoaded( false ),
m_closingLoop( 0 ),
m_scripter( 0 ),
m_archiveData( 0 ),
m_fontsCached( false )
{
calculateMaxTextPages();
@ -109,6 +111,7 @@ class DocumentPrivate
ConfigInterface* generatorConfig( GeneratorInfo& info );
SaveInterface* generatorSave( GeneratorInfo& info );
bool openDocumentInternal( const KService::Ptr& offer, bool isstdin, const QString& docFile, const QByteArray& filedata );
bool savePageDocumentInfo( KTemporaryFile *infoFile, int what ) const;
// private slots
void saveDocumentInfo() const;
@ -211,6 +214,8 @@ class DocumentPrivate
Scripter *m_scripter;
ArchiveData *m_archiveData;
QPointer< FontExtractionThread > m_fontThread;
bool m_fontsCached;
FontInfo::List m_fontsCache;