compile
[kdegraphics.git] / okular / core / textdocumentgenerator.cpp
blobff3a6de955fd8b8b3d3e170577b9a529afc57552
1 /***************************************************************************
2 * Copyright (C) 2007 by Tobias Koenig <tokoe@kde.org> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 ***************************************************************************/
10 #include "textdocumentgenerator.h"
11 #include "textdocumentgenerator_p.h"
13 #include <QtCore/QFile>
14 #include <QtCore/QMutex>
15 #include <QtCore/QStack>
16 #include <QtCore/QTextStream>
17 #include <QtCore/QVector>
18 #include <QtGui/QFontDatabase>
19 #include <QtGui/QImage>
20 #include <QtGui/QPainter>
21 #include <QtGui/QPrinter>
22 #if QT_VERSION >= 0x040500
23 #include <QtGui/QTextDocumentWriter>
24 #endif
26 #include <okular/core/action.h>
27 #include <okular/core/annotations.h>
28 #include <okular/core/page.h>
29 #include <okular/core/textpage.h>
31 #include "document.h"
33 using namespace Okular;
35 /**
36 * Generic Converter Implementation
38 TextDocumentConverter::TextDocumentConverter()
39 : QObject( 0 ), d_ptr( new TextDocumentConverterPrivate )
43 TextDocumentConverter::~TextDocumentConverter()
45 delete d_ptr;
48 DocumentViewport TextDocumentConverter::calculateViewport( QTextDocument *document, const QTextBlock &block )
50 return TextDocumentUtils::calculateViewport( document, block );
53 TextDocumentGenerator* TextDocumentConverter::generator() const
55 return d_ptr->mParent ? d_ptr->mParent->q_func() : 0;
58 /**
59 * Generic Generator Implementation
61 Okular::TextPage* TextDocumentGeneratorPrivate::createTextPage( int pageNumber ) const
63 #ifdef OKULAR_TEXTDOCUMENT_THREADED_RENDERING
64 Q_Q( const TextDocumentGenerator );
65 #endif
67 Okular::TextPage *textPage = new Okular::TextPage;
69 int start, end;
71 #ifdef OKULAR_TEXTDOCUMENT_THREADED_RENDERING
72 q->userMutex()->lock();
73 #endif
74 TextDocumentUtils::calculatePositions( mDocument, pageNumber, start, end );
77 QTextCursor cursor( mDocument );
78 for ( int i = start; i < end - 1; ++i ) {
79 cursor.setPosition( i );
80 cursor.setPosition( i + 1, QTextCursor::KeepAnchor );
82 QString text = cursor.selectedText();
83 if ( text.length() == 1 ) {
84 QRectF rect;
85 TextDocumentUtils::calculateBoundingRect( mDocument, i, i + 1, rect, pageNumber );
86 if ( pageNumber == -1 )
87 text = "\n";
89 textPage->append( text, new Okular::NormalizedRect( rect.left(), rect.top(), rect.right(), rect.bottom() ) );
93 #ifdef OKULAR_TEXTDOCUMENT_THREADED_RENDERING
94 q->userMutex()->unlock();
95 #endif
97 return textPage;
100 void TextDocumentGeneratorPrivate::addAction( Action *action, int cursorBegin, int cursorEnd )
102 if ( !action )
103 return;
105 LinkPosition position;
106 position.link = action;
107 position.startPosition = cursorBegin;
108 position.endPosition = cursorEnd;
110 mLinkPositions.append( position );
113 void TextDocumentGeneratorPrivate::addAnnotation( Annotation *annotation, int cursorBegin, int cursorEnd )
115 if ( !annotation )
116 return;
118 annotation->setFlags( annotation->flags() | Okular::Annotation::External );
120 AnnotationPosition position;
121 position.annotation = annotation;
122 position.startPosition = cursorBegin;
123 position.endPosition = cursorEnd;
125 mAnnotationPositions.append( position );
128 void TextDocumentGeneratorPrivate::addTitle( int level, const QString &title, const QTextBlock &block )
130 TitlePosition position;
131 position.level = level;
132 position.title = title;
133 position.block = block;
135 mTitlePositions.append( position );
138 void TextDocumentGeneratorPrivate::addMetaData( const QString &key, const QString &value, const QString &title )
140 mDocumentInfo.set( key, value, title );
143 void TextDocumentGeneratorPrivate::addMetaData( DocumentInfo::Key key, const QString &value )
145 mDocumentInfo.set( key, value );
148 void TextDocumentGeneratorPrivate::generateLinkInfos()
150 for ( int i = 0; i < mLinkPositions.count(); ++i ) {
151 const LinkPosition &linkPosition = mLinkPositions[ i ];
153 LinkInfo info;
154 info.link = linkPosition.link;
156 TextDocumentUtils::calculateBoundingRect( mDocument, linkPosition.startPosition, linkPosition.endPosition,
157 info.boundingRect, info.page );
159 if ( info.page >= 0 )
160 mLinkInfos.append( info );
164 void TextDocumentGeneratorPrivate::generateAnnotationInfos()
166 for ( int i = 0; i < mAnnotationPositions.count(); ++i ) {
167 const AnnotationPosition &annotationPosition = mAnnotationPositions[ i ];
169 AnnotationInfo info;
170 info.annotation = annotationPosition.annotation;
172 TextDocumentUtils::calculateBoundingRect( mDocument, annotationPosition.startPosition, annotationPosition.endPosition,
173 info.boundingRect, info.page );
175 if ( info.page >= 0 )
176 mAnnotationInfos.append( info );
180 void TextDocumentGeneratorPrivate::generateTitleInfos()
182 QStack< QPair<int,QDomNode> > parentNodeStack;
184 QDomNode parentNode = mDocumentSynopsis;
186 parentNodeStack.push( qMakePair( 0, parentNode ) );
188 for ( int i = 0; i < mTitlePositions.count(); ++i ) {
189 const TitlePosition &position = mTitlePositions[ i ];
191 Okular::DocumentViewport viewport = TextDocumentUtils::calculateViewport( mDocument, position.block );
193 QDomElement item = mDocumentSynopsis.createElement( position.title );
194 item.setAttribute( "Viewport", viewport.toString() );
196 int headingLevel = position.level;
198 // we need a parent, which has to be at a higher heading level than this heading level
199 // so we just work through the stack
200 while ( ! parentNodeStack.isEmpty() ) {
201 int parentLevel = parentNodeStack.top().first;
202 if ( parentLevel < headingLevel ) {
203 // this is OK as a parent
204 parentNode = parentNodeStack.top().second;
205 break;
206 } else {
207 // we'll need to be further into the stack
208 parentNodeStack.pop();
211 parentNode.appendChild( item );
212 parentNodeStack.push( qMakePair( headingLevel, QDomNode(item) ) );
216 TextDocumentGenerator::TextDocumentGenerator( TextDocumentConverter *converter, QObject *parent, const QVariantList &args )
217 : Okular::Generator( *new TextDocumentGeneratorPrivate( converter ), parent, args )
219 converter->d_ptr->mParent = d_func();
221 setFeature( TextExtraction );
222 setFeature( PrintNative );
223 setFeature( PrintToFile );
224 #ifdef OKULAR_TEXTDOCUMENT_THREADED_RENDERING
225 if ( QFontDatabase::supportsThreadedFontRendering() )
226 setFeature( Threaded );
227 #endif
229 connect( converter, SIGNAL( addAction( Action*, int, int ) ),
230 this, SLOT( addAction( Action*, int, int ) ) );
231 connect( converter, SIGNAL( addAnnotation( Annotation*, int, int ) ),
232 this, SLOT( addAnnotation( Annotation*, int, int ) ) );
233 connect( converter, SIGNAL( addTitle( int, const QString&, const QTextBlock& ) ),
234 this, SLOT( addTitle( int, const QString&, const QTextBlock& ) ) );
235 connect( converter, SIGNAL( addMetaData( const QString&, const QString&, const QString& ) ),
236 this, SLOT( addMetaData( const QString&, const QString&, const QString& ) ) );
237 connect( converter, SIGNAL( addMetaData( DocumentInfo::Key, const QString& ) ),
238 this, SLOT( addMetaData( DocumentInfo::Key, const QString& ) ) );
240 connect( converter, SIGNAL( error( const QString&, int ) ),
241 this, SIGNAL( error( const QString&, int ) ) );
242 connect( converter, SIGNAL( warning( const QString&, int ) ),
243 this, SIGNAL( warning( const QString&, int ) ) );
244 connect( converter, SIGNAL( notice( const QString&, int ) ),
245 this, SIGNAL( notice( const QString&, int ) ) );
248 TextDocumentGenerator::~TextDocumentGenerator()
252 bool TextDocumentGenerator::loadDocument( const QString & fileName, QVector<Okular::Page*> & pagesVector )
254 Q_D( TextDocumentGenerator );
255 d->mDocument = d->mConverter->convert( fileName );
257 if ( !d->mDocument )
259 // loading failed, cleanup all the stuff eventually gathered from the converter
260 d->mTitlePositions.clear();
261 Q_FOREACH ( const TextDocumentGeneratorPrivate::LinkPosition &linkPos, d->mLinkPositions )
263 delete linkPos.link;
265 d->mLinkPositions.clear();
266 Q_FOREACH ( const TextDocumentGeneratorPrivate::AnnotationPosition &annPos, d->mAnnotationPositions )
268 delete annPos.annotation;
270 d->mAnnotationPositions.clear();
272 return false;
275 d->generateTitleInfos();
276 d->generateLinkInfos();
277 d->generateAnnotationInfos();
279 pagesVector.resize( d->mDocument->pageCount() );
281 const QSize size = d->mDocument->pageSize().toSize();
283 QVector< QLinkedList<Okular::ObjectRect*> > objects( d->mDocument->pageCount() );
284 for ( int i = 0; i < d->mLinkInfos.count(); ++i ) {
285 const TextDocumentGeneratorPrivate::LinkInfo &info = d->mLinkInfos.at( i );
287 const QRectF rect = info.boundingRect;
288 objects[ info.page ].append( new Okular::ObjectRect( rect.left(), rect.top(), rect.right(), rect.bottom(), false,
289 Okular::ObjectRect::Action, info.link ) );
292 QVector< QLinkedList<Okular::Annotation*> > annots( d->mDocument->pageCount() );
293 for ( int i = 0; i < d->mAnnotationInfos.count(); ++i ) {
294 const TextDocumentGeneratorPrivate::AnnotationInfo &info = d->mAnnotationInfos[ i ];
296 QRect rect( 0, info.page * size.height(), size.width(), size.height() );
297 info.annotation->setBoundingRectangle( Okular::NormalizedRect( rect.left(), rect.top(), rect.right(), rect.bottom() ) );
298 annots[ info.page ].append( info.annotation );
301 for ( int i = 0; i < d->mDocument->pageCount(); ++i ) {
302 Okular::Page * page = new Okular::Page( i, size.width(), size.height(), Okular::Rotation0 );
303 pagesVector[ i ] = page;
305 if ( !objects.at( i ).isEmpty() ) {
306 page->setObjectRects( objects.at( i ) );
308 QLinkedList<Okular::Annotation*>::ConstIterator annIt = annots.at( i ).begin(), annEnd = annots.at( i ).end();
309 for ( ; annIt != annEnd; ++annIt ) {
310 page->addAnnotation( *annIt );
314 return true;
317 bool TextDocumentGenerator::doCloseDocument()
319 Q_D( TextDocumentGenerator );
320 delete d->mDocument;
321 d->mDocument = 0;
323 d->mTitlePositions.clear();
324 d->mLinkPositions.clear();
325 d->mLinkInfos.clear();
326 d->mAnnotationPositions.clear();
327 d->mAnnotationInfos.clear();
328 // do not use clear() for the following two, otherwise they change type
329 d->mDocumentInfo = Okular::DocumentInfo();
330 d->mDocumentSynopsis = Okular::DocumentSynopsis();
332 return true;
335 bool TextDocumentGenerator::canGeneratePixmap() const
337 return Generator::canGeneratePixmap();
340 void TextDocumentGenerator::generatePixmap( Okular::PixmapRequest * request )
342 Generator::generatePixmap( request );
345 QImage TextDocumentGeneratorPrivate::image( PixmapRequest * request )
347 if ( !mDocument )
348 return QImage();
350 #ifdef OKULAR_TEXTDOCUMENT_THREADED_RENDERING
351 Q_Q( TextDocumentGenerator );
352 #endif
354 QImage image( request->width(), request->height(), QImage::Format_ARGB32 );
355 image.fill( Qt::white );
357 QPainter p;
358 p.begin( &image );
360 qreal width = request->width();
361 qreal height = request->height();
363 const QSize size = mDocument->pageSize().toSize();
365 p.scale( width / (qreal)size.width(), height / (qreal)size.height() );
367 QRect rect;
368 rect = QRect( 0, request->pageNumber() * size.height(), size.width(), size.height() );
369 p.translate( QPoint( 0, request->pageNumber() * size.height() * -1 ) );
370 #ifdef OKULAR_TEXTDOCUMENT_THREADED_RENDERING
371 q->userMutex()->lock();
372 #endif
373 mDocument->drawContents( &p, rect );
374 #ifdef OKULAR_TEXTDOCUMENT_THREADED_RENDERING
375 q->userMutex()->unlock();
376 #endif
377 p.end();
379 return image;
382 Okular::TextPage* TextDocumentGenerator::textPage( Okular::Page * page )
384 Q_D( TextDocumentGenerator );
385 return d->createTextPage( page->number() );
388 bool TextDocumentGenerator::print( QPrinter& printer )
390 Q_D( TextDocumentGenerator );
391 if ( !d->mDocument )
392 return false;
394 d->mDocument->print( &printer );
396 return true;
399 const Okular::DocumentInfo* TextDocumentGenerator::generateDocumentInfo()
401 Q_D( TextDocumentGenerator );
402 return &d->mDocumentInfo;
405 const Okular::DocumentSynopsis* TextDocumentGenerator::generateDocumentSynopsis()
407 Q_D( TextDocumentGenerator );
408 if ( !d->mDocumentSynopsis.hasChildNodes() )
409 return 0;
410 else
411 return &d->mDocumentSynopsis;
414 QVariant TextDocumentGeneratorPrivate::metaData( const QString &key, const QVariant &option ) const
416 Q_UNUSED( option )
417 if ( key == "DocumentTitle" )
419 return mDocumentInfo.get( "title" );
421 return QVariant();
424 Okular::ExportFormat::List TextDocumentGenerator::exportFormats( ) const
426 static Okular::ExportFormat::List formats;
427 if ( formats.isEmpty() ) {
428 formats.append( Okular::ExportFormat::standardFormat( Okular::ExportFormat::PlainText ) );
429 formats.append( Okular::ExportFormat::standardFormat( Okular::ExportFormat::PDF ) );
430 #if QT_VERSION >= 0x040500
431 if ( QTextDocumentWriter::supportedDocumentFormats().contains( "ODF" ) ) {
432 formats.append( Okular::ExportFormat::standardFormat( Okular::ExportFormat::OpenDocumentText ) );
434 if ( QTextDocumentWriter::supportedDocumentFormats().contains( "HTML" ) ) {
435 formats.append( Okular::ExportFormat::standardFormat( Okular::ExportFormat::HTML ) );
437 #endif
440 return formats;
443 bool TextDocumentGenerator::exportTo( const QString &fileName, const Okular::ExportFormat &format )
445 Q_D( TextDocumentGenerator );
446 if ( !d->mDocument )
447 return false;
449 if ( format.mimeType()->name() == QLatin1String( "application/pdf" ) ) {
450 QFile file( fileName );
451 if ( !file.open( QIODevice::WriteOnly ) )
452 return false;
454 QPrinter printer( QPrinter::HighResolution );
455 printer.setOutputFormat( QPrinter::PdfFormat );
456 printer.setOutputFileName( fileName );
457 d->mDocument->print( &printer );
459 return true;
460 } else if ( format.mimeType()->name() == QLatin1String( "text/plain" ) ) {
461 QFile file( fileName );
462 if ( !file.open( QIODevice::WriteOnly ) )
463 return false;
465 QTextStream out( &file );
466 out << d->mDocument->toPlainText();
468 return true;
469 #if QT_VERSION >= 0x040500
470 } else if ( format.mimeType()->name() == QLatin1String( "application/vnd.oasis.opendocument.text" ) ) {
471 QTextDocumentWriter odfWriter( fileName, "odf" );
473 return odfWriter.write( d->mDocument );
474 } else if ( format.mimeType()->name() == QLatin1String( "text/html" ) ) {
475 QTextDocumentWriter odfWriter( fileName, "html" );
477 return odfWriter.write( d->mDocument );
478 #endif
480 return false;
483 #include "textdocumentgenerator.moc"