File indexing completed on 2024-05-12 16:28:30

0001 /* This file is part of the KDE project
0002    Copyright (C) 2010 KO GmbH <jos.van.den.oever@kogmbh.com>
0003    Copyright (C) 2012 Sven Langkamp <sven.langkamp@gmail.com>
0004    Copyright (C) 2015-2016 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Library General Public
0008    License as published by the Free Software Foundation; either
0009    version 2 of the License, or (at your option) any later version.
0010 
0011    This library is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014    Library General Public License for more details.
0015 
0016    You should have received a copy of the GNU Library General Public License
0017    along with this library; see the file COPYING.LIB.  If not, write to
0018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019    Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #include "OkularOdtGenerator.h"
0023 
0024 #include <QDebug>
0025 #include <QImage>
0026 #include <QPainter>
0027 #include <QTextDocument>
0028 #include <QMimeDatabase>
0029 #include <QMimeType>
0030 
0031 #include <KoDocumentEntry.h>
0032 #include <KoPart.h>
0033 #include <KWDocument.h>
0034 #include <KWPage.h>
0035 #include <KWCanvasItem.h>
0036 #include <frames/KWTextFrameSet.h>
0037 #include <KoShapeManager.h>
0038 #include <KoDocumentInfo.h>
0039 #include <KoGlobal.h>
0040 #include <KoParagraphStyle.h>
0041 #include <KoTextLayoutRootArea.h>
0042 #include <KoCharAreaInfo.h>
0043 
0044 #include <okular/core/page.h>
0045 
0046 
0047 OkularOdtGenerator::OkularOdtGenerator( QObject *parent, const QVariantList &args )
0048     : Okular::Generator( parent, args )
0049 {
0050     m_doc = 0;
0051     setFeature( TextExtraction );
0052 }
0053 
0054 OkularOdtGenerator::~OkularOdtGenerator()
0055 {
0056 }
0057 
0058 static Okular::DocumentViewport calculateViewport( const QTextBlock &block,
0059                                                    KoTextDocumentLayout* textDocumentLayout )
0060 {
0061     KoTextLayoutRootArea *rootArea = textDocumentLayout->rootAreaForPosition(block.position());
0062 
0063     QRectF rect = textDocumentLayout->blockBoundingRect( block );
0064     rect.translate(-(rootArea->referenceRect().topLeft()));
0065 
0066     KoShape *shape = rootArea->associatedShape();
0067     rect.translate(shape->absolutePosition(KoFlake::TopLeftCorner));
0068 
0069     KWPage* page = static_cast<KWPage *>(rootArea->page());
0070     rect.translate(static_cast<qreal>(0.0), -(page->offsetInDocument()));
0071 
0072     const qreal pageHeight = page->height();
0073     const qreal pageWidth = page->width();
0074     const int pageNumber = page->pageNumber();
0075 
0076     Okular::DocumentViewport viewport( pageNumber-1 );
0077     viewport.rePos.normalizedX = static_cast<double>(rect.x()) / static_cast<double>(pageWidth);
0078     viewport.rePos.normalizedY = static_cast<double>(rect.y()) / static_cast<double>(pageHeight);
0079     viewport.rePos.enabled = true;
0080     viewport.rePos.pos = Okular::DocumentViewport::TopLeft;
0081 
0082     return viewport;
0083 }
0084 
0085 bool OkularOdtGenerator::loadDocument( const QString &fileName, QVector<Okular::Page*> &pages )
0086 {
0087     const QString mimetype = QMimeDatabase().mimeTypeForFile(fileName).name();
0088 
0089     QString error;
0090     KoDocumentEntry documentEntry = KoDocumentEntry::queryByMimeType(mimetype);
0091     KoPart *part = documentEntry.createKoPart(&error);
0092 
0093     if (!error.isEmpty()) {
0094         qWarning() << "Error creating document" << mimetype << error;
0095         return 0;
0096     }
0097 
0098     m_doc = static_cast<KWDocument*>(part->document());
0099     const QUrl url = QUrl::fromLocalFile(fileName);
0100     m_doc->setCheckAutoSaveFile(false);
0101     m_doc->setAutoErrorHandlingEnabled(false); // show error dialogs
0102     if (!m_doc->openUrl(url)) {
0103         return false;
0104     }
0105 
0106     while (!m_doc->layoutFinishedAtleastOnce()) {
0107         QCoreApplication::processEvents();
0108 
0109         if (!QCoreApplication::hasPendingEvents())
0110             break;
0111     }
0112 
0113     KWPageManager *pageManager = m_doc->pageManager();
0114     int pageCount = pageManager->pages().count();
0115     for(int i = 1; i <= pageCount; ++i) {
0116 
0117         KWPage kwpage = pageManager->page(i);
0118 
0119         Okular::Page * page = new Okular::Page( i-1, kwpage.width(), kwpage.height(), Okular::Rotation0 );
0120         pages.append(page);
0121     }
0122 
0123     // meta data
0124     const KoDocumentInfo *documentInfo = m_doc->documentInfo();
0125     m_documentInfo.set( Okular::DocumentInfo::MimeType, mimetype );
0126     m_documentInfo.set( Okular::DocumentInfo::Producer, documentInfo->originalGenerator() );
0127     m_documentInfo.set( Okular::DocumentInfo::Title,       documentInfo->aboutInfo("title") );
0128     m_documentInfo.set( Okular::DocumentInfo::Subject,     documentInfo->aboutInfo("subject") );
0129     m_documentInfo.set( Okular::DocumentInfo::Keywords,     documentInfo->aboutInfo("keyword") );
0130     m_documentInfo.set( Okular::DocumentInfo::Description, documentInfo->aboutInfo("description") );
0131     m_documentInfo.set( "language",    KoGlobal::languageFromTag(documentInfo->aboutInfo("language")),  i18n("Language"));
0132 
0133     const QString creationDate = documentInfo->aboutInfo("creation-date");
0134     if (!creationDate.isEmpty()) {
0135         QDateTime t = QDateTime::fromString(creationDate, Qt::ISODate);
0136         m_documentInfo.set( Okular::DocumentInfo::CreationDate, QLocale().toString(t, QLocale::ShortFormat) );
0137     }
0138     m_documentInfo.set( Okular::DocumentInfo::Creator,  documentInfo->aboutInfo("initial-creator") );
0139 
0140     const QString modificationDate = documentInfo->aboutInfo("date");
0141     if (!modificationDate.isEmpty()) {
0142         QDateTime t = QDateTime::fromString(modificationDate, Qt::ISODate);
0143         m_documentInfo.set( Okular::DocumentInfo::ModificationDate, QLocale().toString(t, QLocale::ShortFormat) );
0144     }
0145     m_documentInfo.set( Okular::DocumentInfo::Author, documentInfo->aboutInfo("creator") );
0146 
0147     // ToC
0148     QDomNode parentNode = m_documentSynopsis;
0149 
0150     QStack< QPair<int,QDomNode> > parentNodeStack;
0151     parentNodeStack.push( qMakePair( 0, parentNode ) );
0152 
0153     QTextDocument* textDocument = m_doc->mainFrameSet()->document();
0154     KoTextDocumentLayout* textDocumentLayout = static_cast<KoTextDocumentLayout *>(textDocument->documentLayout());
0155 
0156     QTextBlock block = textDocument->begin();
0157     for (; block.isValid(); block = block.next()) {
0158         int blockLevel = 0;
0159         if (block.blockFormat().hasProperty(KoParagraphStyle::OutlineLevel)) {
0160             blockLevel = block.blockFormat().intProperty(KoParagraphStyle::OutlineLevel);
0161         }
0162 
0163         // no blockLevel yet?
0164         if (blockLevel == 0) {
0165             continue;
0166         }
0167 
0168         Okular::DocumentViewport viewport = calculateViewport( block, textDocumentLayout );
0169 
0170         QDomElement item = m_documentSynopsis.createElement( block.text() );
0171         item.setAttribute( "Viewport", viewport.toString() );
0172 
0173         // we need a parent, which has to be at a higher heading level than this heading level
0174         // so we just work through the stack
0175         while ( ! parentNodeStack.isEmpty() ) {
0176             int parentLevel = parentNodeStack.top().first;
0177             if ( parentLevel < blockLevel ) {
0178                 // this is OK as a parent
0179                 parentNode = parentNodeStack.top().second;
0180                 break;
0181             } else {
0182                 // we'll need to be further into the stack
0183                 parentNodeStack.pop();
0184             }
0185         }
0186         parentNode.appendChild( item );
0187         parentNodeStack.push( qMakePair( blockLevel, QDomNode(item) ) );
0188     }
0189 
0190     return true;
0191 }
0192 
0193 bool OkularOdtGenerator::doCloseDocument()
0194 {
0195     delete m_doc;
0196     m_doc = 0;
0197 
0198     m_documentInfo = Okular::DocumentInfo();
0199     m_documentSynopsis = Okular::DocumentSynopsis();
0200 
0201     return true;
0202 }
0203 
0204 bool OkularOdtGenerator::canGeneratePixmap() const
0205 {
0206     return true;
0207 }
0208 
0209 void OkularOdtGenerator::generatePixmap( Okular::PixmapRequest *request )
0210 {
0211     QPixmap* pix;
0212     if (!m_doc) {
0213         pix = new QPixmap(request->width(), request->height());
0214         QPainter painter(pix);
0215         painter.fillRect(0 ,0 , request->width(), request->height(), Qt::white);
0216     } else {
0217 
0218         // use shape manager from canvasItem even for QWidget environments
0219         // if using the shape manager from one of the views there is no guarantee
0220         // that the view, its canvas and the shapemanager is not destroyed in between
0221         KoShapeManager* shapeManager = static_cast<KWCanvasItem*>(m_doc->documentPart()->canvasItem(m_doc))->shapeManager();
0222 
0223         KWPageManager *pageManager = m_doc->pageManager();
0224 
0225         KWPage page = pageManager->page(request->pageNumber()+1);
0226 
0227         pix = new QPixmap(request->width(), request->height());
0228         QPainter painter(pix);
0229 
0230         QSize rSize(request->width(), request->height());
0231 
0232         pix = new QPixmap();
0233         pix->convertFromImage(page.thumbnail(rSize, shapeManager, true));
0234     }
0235 
0236     request->page()->setPixmap( request->observer(), pix );
0237 
0238     signalPixmapRequestDone( request );
0239 }
0240 
0241 bool OkularOdtGenerator::canGenerateTextPage() const
0242 {
0243     return true;
0244 }
0245 
0246 Okular::TextPage* OkularOdtGenerator::textPage( Okular::Page *page )
0247 {
0248     QTextDocument* textDocument = m_doc->mainFrameSet()->document();
0249     KoTextDocumentLayout* textDocumentLayout = static_cast<KoTextDocumentLayout *>(textDocument->documentLayout());
0250 
0251     KoTextLayoutRootArea *rootArea = 0;
0252     foreach(KoTextLayoutRootArea *area, textDocumentLayout->rootAreas()) {
0253         if (area->page()->pageNumber() == page->number()+1) {
0254             rootArea = area;
0255             break;
0256         }
0257     }
0258 
0259     if (!rootArea) {
0260         return 0;
0261     }
0262 
0263     const QVector<KoCharAreaInfo> charAreaInfos = rootArea->generateCharAreaInfos();
0264 
0265     // TODO: text from master pages (headers/footers), text in floating shapes
0266     if (charAreaInfos.isEmpty()) {
0267         return 0;
0268     }
0269 
0270     KWPage* wpage = static_cast<KWPage *>(rootArea->page());
0271     KoShape *shape = rootArea->associatedShape();
0272 
0273     const QPointF offset = shape->absolutePosition(KoFlake::TopLeftCorner)
0274                            + QPointF(static_cast<qreal>(0.0), -(wpage->offsetInDocument()))
0275                            - rootArea->referenceRect().topLeft();
0276 
0277     const double pageHeight = wpage->height();
0278     const double pageWidth = wpage->width();
0279 
0280     Okular::TextPage *textPage = new Okular::TextPage;
0281     foreach(const KoCharAreaInfo &charAreaInfo, charAreaInfos) {
0282         const QRectF rect = charAreaInfo.rect.translated(offset);
0283         const double left = static_cast<double>(rect.left()) / pageWidth;
0284         const double top = static_cast<double>(rect.top()) / pageHeight;
0285         const double right = static_cast<double>(rect.right()) / pageWidth;
0286         const double bottom = static_cast<double>(rect.bottom()) / pageHeight;
0287         textPage->append( charAreaInfo.character,
0288                           new Okular::NormalizedRect( left, top, right, bottom ) );
0289     }
0290     return textPage;
0291 }
0292 
0293 
0294 Okular::DocumentInfo OkularOdtGenerator::generateDocumentInfo( const QSet<Okular::DocumentInfo::Key> &keys ) const
0295 {
0296     Q_UNUSED(keys);
0297 
0298     return m_documentInfo;
0299 }
0300 
0301 const Okular::DocumentSynopsis* OkularOdtGenerator::generateDocumentSynopsis()
0302 {
0303     return m_documentSynopsis.hasChildNodes() ? &m_documentSynopsis : 0;
0304 }