diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -373,6 +373,7 @@ ui/presentationsearchbar.cpp ui/presentationwidget.cpp ui/propertiesdialog.cpp + ui/revisionviewer.cpp ui/searchlineedit.cpp ui/searchwidget.cpp ui/sidebar.cpp diff --git a/core/document.h b/core/document.h --- a/core/document.h +++ b/core/document.h @@ -61,6 +61,7 @@ class SourceReference; class View; class VisiblePageRect; +class SignatureInfo; /** IDs for seaches. Globally defined here. **/ #define PART_SEARCH_ID 1 @@ -1021,6 +1022,13 @@ */ void reloadDocument() const; + /** + * Requests generator to read the part of document covered by a signature into @p buffer. + * + * @since 1.4 + */ + void requestSignedRevisionData( Okular::SignatureInfo *info, QByteArray *buffer ); + Q_SIGNALS: /** * This signal is emitted whenever an action requests a diff --git a/core/document.cpp b/core/document.cpp --- a/core/document.cpp +++ b/core/document.cpp @@ -5058,6 +5058,11 @@ return d->m_generator ? d->m_generator->layersModel() : nullptr; } +void Document::requestSignedRevisionData( Okular::SignatureInfo *info, QByteArray *buffer ) +{ + d->m_generator->requestSignedRevisionData( info, buffer ); +} + void DocumentPrivate::requestDone( PixmapRequest * req ) { if ( !req ) diff --git a/core/generator.h b/core/generator.h --- a/core/generator.h +++ b/core/generator.h @@ -61,6 +61,7 @@ class TextRequestPrivate; class NormalizedRect; class SourceReference; +class SignatureInfo; /* Note: on contents generation and asynchronous queries. * Many observers may want to request data syncronously or asynchronously. @@ -594,6 +595,13 @@ */ QSizeF dpi() const; + /** + * Creates a signed revision using information from @p info and stores the data + * in @p buffer. + * @since 1.4 + */ + virtual void requestSignedRevisionData( SignatureInfo *info, QByteArray *buffer ); + protected Q_SLOTS: /** * Gets the font data for the given font diff --git a/core/generator.cpp b/core/generator.cpp --- a/core/generator.cpp +++ b/core/generator.cpp @@ -527,6 +527,10 @@ } +void Generator::requestSignedRevisionData( SignatureInfo *, QByteArray * ) +{ +} + void Generator::setDPI(const QSizeF & dpi) { Q_D( Generator ); diff --git a/generators/poppler/generator_pdf.h b/generators/poppler/generator_pdf.h --- a/generators/poppler/generator_pdf.h +++ b/generators/poppler/generator_pdf.h @@ -31,6 +31,7 @@ namespace Okular { class ObjectRect; class SourceReference; +class SignatureInfo; } class PDFOptionsPage; @@ -105,6 +106,7 @@ SwapBackingFileResult swapBackingFile( QString const &newFileName, QVector & newPagesVector ) override; bool doCloseDocument() override; Okular::TextPage* textPage( Okular::TextRequest *request ) override; + void requestSignedRevisionData( Okular::SignatureInfo *info, QByteArray *buffer ) override; protected Q_SLOTS: void requestFontData(const Okular::FontInfo &font, QByteArray *data); diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -1264,6 +1264,26 @@ *data = pdfdoc->fontData(fi); } +void PDFGenerator::requestSignedRevisionData( Okular::SignatureInfo *info, QByteArray *buffer ) +{ + Q_ASSERT( info ); + Q_ASSERT( buffer ); + + const QUrl docUrl = document()->currentDocument(); + QFile f( docUrl.toLocalFile() ); + if ( !f.open( QIODevice::ReadOnly ) ) + { + KMessageBox::error( nullptr, i18n("Could not open '%1'. File does not exist", docUrl.toDisplayString() ) ); + return; + } + + QList byteRange = info->signedRangeBounds(); + f.seek( byteRange.first() ); + QDataStream stream( buffer, QIODevice::WriteOnly ); + stream << f.read( byteRange.last() - byteRange.first() ); + f.close(); +} + #define DUMMY_QPRINTER_COPY bool PDFGenerator::print( QPrinter& printer ) { @@ -1473,6 +1493,20 @@ return QVariant::fromValue>(pdfdoc->formCalculateOrder()); #endif } + else if ( key == QLatin1String("IsDigitallySigned") ) + { + const Okular::Document *doc = document(); + uint numPages = doc->pages(); + for ( uint i = 0; i < numPages; i++ ) + { + foreach ( Okular::FormField *f, doc->page( i )->formFields() ) + { + if ( f->type() == Okular::FormField::FormSignature ) + return true; + } + } + return false; + } return QVariant(); } diff --git a/part.cpp b/part.cpp --- a/part.cpp +++ b/part.cpp @@ -1554,6 +1554,12 @@ m_formsMessage->setMessageType( KMessageWidget::Information ); m_formsMessage->setVisible( true ); } + else if ( ok && m_document->metaData( QStringLiteral("IsDigitallySigned") ).toBool() && m_embedMode == PrintPreviewMode ) + { + m_formsMessage->setText( i18n( "All editing and interactive features for this document are disabled. Please save a copy and reopen to edit this document." ) ); + m_formsMessage->setMessageType( KMessageWidget::Information ); + m_formsMessage->setVisible( true ); + } else { m_formsMessage->setVisible( false ); diff --git a/ui/formwidgets.h b/ui/formwidgets.h --- a/ui/formwidgets.h +++ b/ui/formwidgets.h @@ -23,7 +23,7 @@ #include #include #include - +#include class ComboEdit; class QMenu; class QButtonGroup; @@ -38,6 +38,7 @@ class FormFieldButton; class FormFieldChoice; class FormFieldText; +class FormFieldSignature; class Document; } @@ -126,6 +127,7 @@ friend class FileEdit; friend class ListEdit; friend class ComboEdit; + friend class SignatureEdit; QList< RadioData > m_radios; QHash< int, QAbstractButton* > m_buttons; @@ -347,6 +349,13 @@ DECLARE_ADDITIONAL_ACTIONS }; +class SignatureEdit : public QPushButton, public FormWidgetIface +{ + Q_OBJECT +public: + SignatureEdit( Okular::FormFieldSignature *fs, QWidget *p = nullptr ); +}; + #undef DECLARE_ADDITIONAL_ACTIONS #endif diff --git a/ui/formwidgets.cpp b/ui/formwidgets.cpp --- a/ui/formwidgets.cpp +++ b/ui/formwidgets.cpp @@ -23,6 +23,7 @@ #include #include #include +#include // local includes #include "core/form.h" @@ -276,6 +277,11 @@ } break; } + case Okular::FormField::FormSignature: + { + auto fs = static_cast(ff); + widget = new SignatureEdit(fs, parent); + } default: ; } @@ -1052,6 +1058,23 @@ return QComboBox::event( e ); } +#include "core/signatureutils.h" +#include "certificateviewer.h" +#include "revisionviewer.h" + +SignatureEdit::SignatureEdit(Okular::FormFieldSignature *fs, QWidget *p) + : QPushButton("Hello", p), FormWidgetIface(this, fs) +{ + auto sigInfo = fs->validate(); + //auto certInfo = sigInfo->certificateInfo(); + connect(this, &SignatureEdit::clicked, this, [=]{ + QByteArray data; + m_controller->m_doc->requestSignedRevisionData( sigInfo, &data ); + RevisionViewer v(data, this); + v.viewRevision(); + }); +} + // Code for additional action handling. // Challenge: Change preprocessor magic to C++ magic! // diff --git a/ui/revisionviewer.h b/ui/revisionviewer.h new file mode 100644 --- /dev/null +++ b/ui/revisionviewer.h @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright (C) 2018 by Chinmoy Ranjan Pradhan * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#ifndef OKULAR_REVISIONVIEWER_H +#define OKULAR_REVISIONVIEWER_H + +#include +#include + +class QWidget; + +class RevisionViewer : public QObject +{ + Q_OBJECT + + public: + explicit RevisionViewer( const QByteArray &revisionData, QWidget *parent = nullptr ); + + public Q_SLOTS: + void viewRevision(); + + private: + QWidget *m_parent; + QByteArray m_revisionData; +}; + +#endif diff --git a/ui/revisionviewer.cpp b/ui/revisionviewer.cpp new file mode 100644 --- /dev/null +++ b/ui/revisionviewer.cpp @@ -0,0 +1,99 @@ +/*************************************************************************** + * Copyright (C) 2018 by Chinmoy Ranjan Pradhan * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ + +#include "revisionviewer.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fileprinterpreview.h" + +static void clearLayout( QLayout* layout ) +{ + while ( QLayoutItem* item = layout->takeAt(0) ) + { + delete item->widget(); + delete item; + } +} + +class RevisionPreview : public Okular::FilePrinterPreview +{ + Q_OBJECT + + public: + explicit RevisionPreview( const QString &revisionFile, QWidget *parent = nullptr ); + + private Q_SLOTS: + void doSave(); + + private: + QString m_filename; +}; + +RevisionPreview::RevisionPreview( const QString &revisionFile, QWidget *parent ) + : FilePrinterPreview( revisionFile, parent ), m_filename( revisionFile ) +{ + setWindowTitle( i18n("Revision Preview") ); + + auto mainLayout = static_cast( layout() ); + clearLayout( mainLayout ); + auto btnBox = new QDialogButtonBox( QDialogButtonBox::Close, this ); + auto saveBtn = new QPushButton( i18n( "Save As"), this ); + btnBox->button( QDialogButtonBox::Close )->setDefault( true ); + btnBox->addButton( saveBtn, QDialogButtonBox::ActionRole ); + connect( btnBox, &QDialogButtonBox::rejected, this, &RevisionPreview::reject ); + connect( saveBtn, &QPushButton::clicked, this, &RevisionPreview::doSave ); + mainLayout->addWidget( btnBox ); +} + +void RevisionPreview::doSave() +{ + QMimeDatabase db; + QMimeType mime = db.mimeTypeForFile( m_filename ); + const QString caption = i18n( "Where do you want to save this revision?" ); + const QString path = QFileDialog::getSaveFileName( this, caption, QStringLiteral("Revision"), mime.filterString() ); + if ( !path.isEmpty() && !QFile::copy( m_filename, path ) ) + { + KMessageBox::error( this, i18n("Could not save file %1.", path) ); + return; + } +} + +RevisionViewer::RevisionViewer( const QByteArray &revisionData, QWidget *parent ) + : QObject( parent ), m_parent( parent ), m_revisionData( revisionData ) +{ +} + +void RevisionViewer::viewRevision() +{ + QMimeDatabase db; + QMimeType mime = db.mimeTypeForData( m_revisionData ); + const QString tempDir = QStandardPaths::writableLocation( QStandardPaths::TempLocation ); + QTemporaryFile tf( tempDir + QStringLiteral("/okular_revision_XXXXXX.%1").arg( mime.suffixes().first() )); + if ( !tf.open() ) + { + KMessageBox::error( m_parent, i18n("Could not view revision.") ); + return; + } + tf.write( m_revisionData ); + RevisionPreview previewdlg( tf.fileName(), m_parent ); + previewdlg.exec(); +} + +#include "revisionviewer.moc"