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 }