mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Issue #40: Sanitize document
This commit is contained in:
@@ -44,6 +44,9 @@ add_library(Pdf4QtViewer SHARED
|
||||
pdfviewermainwindow.ui
|
||||
pdfviewermainwindowlite.ui
|
||||
pdfviewersettingsdialog.ui
|
||||
pdfsanitizedocumentdialog.ui
|
||||
pdfsanitizedocumentdialog.cpp
|
||||
pdfsanitizedocumentdialog.h
|
||||
pdf4qtviewer.qrc
|
||||
)
|
||||
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include "pdfundoredomanager.h"
|
||||
#include "pdfrendertoimagesdialog.h"
|
||||
#include "pdfoptimizedocumentdialog.h"
|
||||
#include "pdfsanitizedocumentdialog.h"
|
||||
#include "pdfviewersettingsdialog.h"
|
||||
#include "pdfaboutdialog.h"
|
||||
#include "pdfrenderingerrorswidget.h"
|
||||
@@ -447,6 +448,10 @@ void PDFProgramController::initialize(Features features,
|
||||
{
|
||||
connect(action, &QAction::triggered, this, &PDFProgramController::onActionOptimizeTriggered);
|
||||
}
|
||||
if (QAction* action = m_actionManager->getAction(PDFActionManager::Sanitize))
|
||||
{
|
||||
connect(action, &QAction::triggered, this, &PDFProgramController::onActionSanitizeTriggered);
|
||||
}
|
||||
if (QAction* action = m_actionManager->getAction(PDFActionManager::Encryption))
|
||||
{
|
||||
connect(action, &QAction::triggered, this, &PDFProgramController::onActionEncryptionTriggered);
|
||||
@@ -1173,6 +1178,18 @@ void PDFProgramController::onActionOptimizeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
void PDFProgramController::onActionSanitizeTriggered()
|
||||
{
|
||||
PDFSanitizeDocumentDialog dialog(m_pdfDocument.data(), m_mainWindow);
|
||||
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
pdf::PDFDocumentPointer pointer(new pdf::PDFDocument(dialog.takeSanitizedDocument()));
|
||||
pdf::PDFModifiedDocument document(qMove(pointer), m_optionalContentActivity, pdf::PDFModifiedDocument::Reset);
|
||||
onDocumentModified(qMove(document));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFProgramController::onActionEncryptionTriggered()
|
||||
{
|
||||
auto queryPassword = [this](bool* ok)
|
||||
@@ -1492,6 +1509,7 @@ void PDFProgramController::updateActionsAvailability()
|
||||
m_actionManager->setEnabled(PDFActionManager::Print, hasValidDocument && canPrint);
|
||||
m_actionManager->setEnabled(PDFActionManager::RenderToImages, hasValidDocument && canPrint);
|
||||
m_actionManager->setEnabled(PDFActionManager::Optimize, hasValidDocument);
|
||||
m_actionManager->setEnabled(PDFActionManager::Sanitize, hasValidDocument);
|
||||
m_actionManager->setEnabled(PDFActionManager::Encryption, hasValidDocument);
|
||||
m_actionManager->setEnabled(PDFActionManager::Save, hasValidDocument);
|
||||
m_actionManager->setEnabled(PDFActionManager::SaveAs, hasValidDocument);
|
||||
|
@@ -105,6 +105,7 @@ public:
|
||||
SendByMail,
|
||||
RenderToImages,
|
||||
Optimize,
|
||||
Sanitize,
|
||||
Encryption,
|
||||
FitPage,
|
||||
FitWidth,
|
||||
@@ -327,6 +328,7 @@ private:
|
||||
void onActionSendByEMailTriggered();
|
||||
void onActionRenderToImagesTriggered();
|
||||
void onActionOptimizeTriggered();
|
||||
void onActionSanitizeTriggered();
|
||||
void onActionEncryptionTriggered();
|
||||
void onActionFitPageTriggered();
|
||||
void onActionFitWidthTriggered();
|
||||
|
164
Pdf4QtViewer/pdfsanitizedocumentdialog.cpp
Normal file
164
Pdf4QtViewer/pdfsanitizedocumentdialog.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright (C) 2023 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfsanitizedocumentdialog.h"
|
||||
#include "ui_pdfsanitizedocumentdialog.h"
|
||||
|
||||
#include "pdfwidgetutils.h"
|
||||
#include "pdfdocumentwriter.h"
|
||||
#include "pdfdbgheap.h"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QElapsedTimer>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
namespace pdfviewer
|
||||
{
|
||||
|
||||
PDFSanitizeDocumentDialog::PDFSanitizeDocumentDialog(const pdf::PDFDocument* document, QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PDFSanitizeDocumentDialog),
|
||||
m_document(document),
|
||||
m_sanitizer(pdf::PDFDocumentSanitizer::All, nullptr),
|
||||
m_sanitizeButton(nullptr),
|
||||
m_sanitizationInProgress(false),
|
||||
m_wasSanitized(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
auto addCheckBox = [this](QString text, pdf::PDFDocumentSanitizer::SanitizationFlag flag)
|
||||
{
|
||||
QCheckBox* checkBox = new QCheckBox(text, this);
|
||||
checkBox->setChecked(m_sanitizer.getFlags().testFlag(flag));
|
||||
connect(checkBox, &QCheckBox::clicked, this, [this, flag](bool checked) { m_sanitizer.setFlags(m_sanitizer.getFlags().setFlag(flag, checked)); });
|
||||
ui->groupBoxLayout->addWidget(checkBox);
|
||||
};
|
||||
|
||||
addCheckBox(tr("Remove document info"), pdf::PDFDocumentSanitizer::DocumentInfo);
|
||||
addCheckBox(tr("Remove all metadata"), pdf::PDFDocumentSanitizer::Metadata);
|
||||
addCheckBox(tr("Remove outline (bookmarks)"), pdf::PDFDocumentSanitizer::Bookmarks);
|
||||
addCheckBox(tr("Remove file attachments"), pdf::PDFDocumentSanitizer::FileAttachments);
|
||||
addCheckBox(tr("Remove embedded search index"), pdf::PDFDocumentSanitizer::EmbeddedSearchIndex);
|
||||
addCheckBox(tr("Remove comments and other markup annotations"), pdf::PDFDocumentSanitizer::MarkupAnnotations);
|
||||
addCheckBox(tr("Remove page thumbnails"), pdf::PDFDocumentSanitizer::PageThumbnails);
|
||||
|
||||
m_sanitizeButton = ui->buttonBox->addButton(tr("Sanitize"), QDialogButtonBox::ActionRole);
|
||||
|
||||
connect(m_sanitizeButton, &QPushButton::clicked, this, &PDFSanitizeDocumentDialog::onSanitizeButtonClicked);
|
||||
connect(&m_sanitizer, &pdf::PDFDocumentSanitizer::sanitizationStarted, this, &PDFSanitizeDocumentDialog::onSanitizationStarted);
|
||||
connect(&m_sanitizer, &pdf::PDFDocumentSanitizer::sanitizationProgress, this, &PDFSanitizeDocumentDialog::onSanitizationProgress);
|
||||
connect(&m_sanitizer, &pdf::PDFDocumentSanitizer::sanitizationFinished, this, &PDFSanitizeDocumentDialog::onSanitizationFinished);
|
||||
connect(this, &PDFSanitizeDocumentDialog::displaySanitizationInfo, this, &PDFSanitizeDocumentDialog::onDisplaySanitizationInfo);
|
||||
|
||||
pdf::PDFWidgetUtils::scaleWidget(this, QSize(640, 380));
|
||||
updateUi();
|
||||
pdf::PDFWidgetUtils::style(this);
|
||||
}
|
||||
|
||||
PDFSanitizeDocumentDialog::~PDFSanitizeDocumentDialog()
|
||||
{
|
||||
Q_ASSERT(!m_sanitizationInProgress);
|
||||
Q_ASSERT(!m_future.isRunning());
|
||||
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PDFSanitizeDocumentDialog::sanitize()
|
||||
{
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
m_sanitizer.setDocument(m_document);
|
||||
m_sanitizer.sanitize();
|
||||
m_sanitizedDocument = m_sanitizer.takeSanitizedDocument();
|
||||
|
||||
qreal msecsElapsed = timer.nsecsElapsed() / 1000000.0;
|
||||
timer.invalidate();
|
||||
|
||||
m_sanitizationInfo.msecsElapsed = msecsElapsed;
|
||||
m_sanitizationInfo.bytesBeforeSanitization = pdf::PDFDocumentWriter::getDocumentFileSize(m_document);
|
||||
m_sanitizationInfo.bytesAfterSanitization = pdf::PDFDocumentWriter::getDocumentFileSize(&m_sanitizedDocument);
|
||||
Q_EMIT displaySanitizationInfo();
|
||||
}
|
||||
|
||||
void PDFSanitizeDocumentDialog::onSanitizeButtonClicked()
|
||||
{
|
||||
Q_ASSERT(!m_sanitizationInProgress);
|
||||
Q_ASSERT(!m_future.isRunning());
|
||||
|
||||
m_sanitizationInProgress = true;
|
||||
m_future = QtConcurrent::run([this]() { sanitize(); });
|
||||
updateUi();
|
||||
}
|
||||
|
||||
void PDFSanitizeDocumentDialog::onSanitizationStarted()
|
||||
{
|
||||
Q_ASSERT(m_sanitizationInProgress);
|
||||
ui->logTextEdit->setPlainText(tr("Sanitization started!"));
|
||||
}
|
||||
|
||||
void PDFSanitizeDocumentDialog::onSanitizationProgress(QString progressText)
|
||||
{
|
||||
Q_ASSERT(m_sanitizationInProgress);
|
||||
ui->logTextEdit->setPlainText(QString("%1\n%2").arg(ui->logTextEdit->toPlainText(), progressText));
|
||||
}
|
||||
|
||||
void PDFSanitizeDocumentDialog::onSanitizationFinished()
|
||||
{
|
||||
ui->logTextEdit->setPlainText(QString("%1\n%2").arg(ui->logTextEdit->toPlainText(), tr("Sanitization finished!")));
|
||||
m_future.waitForFinished();
|
||||
m_sanitizationInProgress = false;
|
||||
m_wasSanitized = true;
|
||||
updateUi();
|
||||
}
|
||||
|
||||
void PDFSanitizeDocumentDialog::onDisplaySanitizationInfo()
|
||||
{
|
||||
QStringList texts;
|
||||
texts << tr("Sanitized in %1 msecs").arg(m_sanitizationInfo.msecsElapsed);
|
||||
if (m_sanitizationInfo.bytesBeforeSanitization != -1 &&
|
||||
m_sanitizationInfo.bytesAfterSanitization != -1)
|
||||
{
|
||||
texts << tr("Bytes before sanitization: %1").arg(m_sanitizationInfo.bytesBeforeSanitization);
|
||||
texts << tr("Bytes after sanitization: %1").arg(m_sanitizationInfo.bytesAfterSanitization);
|
||||
texts << tr("Bytes saved by sanitization: %1").arg(m_sanitizationInfo.bytesBeforeSanitization - m_sanitizationInfo.bytesAfterSanitization);
|
||||
|
||||
qreal ratio = 100.0;
|
||||
if (m_sanitizationInfo.bytesBeforeSanitization > 0)
|
||||
{
|
||||
ratio = 100.0 * qreal(m_sanitizationInfo.bytesAfterSanitization) / qreal(m_sanitizationInfo.bytesBeforeSanitization);
|
||||
}
|
||||
|
||||
texts << tr("Compression ratio: %1 %").arg(ratio);
|
||||
}
|
||||
ui->logTextEdit->setPlainText(QString("%1\n%2").arg(ui->logTextEdit->toPlainText(), texts.join("\n")));
|
||||
}
|
||||
|
||||
void PDFSanitizeDocumentDialog::updateUi()
|
||||
{
|
||||
for (QCheckBox* checkBox : findChildren<QCheckBox*>(QString(), Qt::FindChildrenRecursively))
|
||||
{
|
||||
checkBox->setEnabled(!m_sanitizationInProgress);
|
||||
}
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_wasSanitized && !m_sanitizationInProgress);
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(!m_sanitizationInProgress);
|
||||
m_sanitizeButton->setEnabled(!m_sanitizationInProgress);
|
||||
}
|
||||
|
||||
} // namespace pdfviewer
|
77
Pdf4QtViewer/pdfsanitizedocumentdialog.h
Normal file
77
Pdf4QtViewer/pdfsanitizedocumentdialog.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2023 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
// PDF4QT is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// with the written consent of the copyright owner, any later version.
|
||||
//
|
||||
// PDF4QT is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFSANITIZEDOCUMENTDIALOG_H
|
||||
#define PDFSANITIZEDOCUMENTDIALOG_H
|
||||
|
||||
#include "pdfdocumentsanitizer.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QFuture>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PDFSanitizeDocumentDialog;
|
||||
}
|
||||
|
||||
namespace pdfviewer
|
||||
{
|
||||
|
||||
class PDFSanitizeDocumentDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFSanitizeDocumentDialog(const pdf::PDFDocument* document, QWidget* parent);
|
||||
virtual ~PDFSanitizeDocumentDialog() override;
|
||||
|
||||
pdf::PDFDocument takeSanitizedDocument() { return qMove(m_sanitizedDocument); }
|
||||
|
||||
signals:
|
||||
void displaySanitizationInfo();
|
||||
|
||||
private:
|
||||
void sanitize();
|
||||
void onSanitizeButtonClicked();
|
||||
void onSanitizationStarted();
|
||||
void onSanitizationProgress(QString progressText);
|
||||
void onSanitizationFinished();
|
||||
void onDisplaySanitizationInfo();
|
||||
|
||||
void updateUi();
|
||||
|
||||
struct SanitizationInfo
|
||||
{
|
||||
qreal msecsElapsed = 0.0;
|
||||
qint64 bytesBeforeSanitization = -1;
|
||||
qint64 bytesAfterSanitization = -1;
|
||||
};
|
||||
|
||||
Ui::PDFSanitizeDocumentDialog* ui;
|
||||
const pdf::PDFDocument* m_document;
|
||||
pdf::PDFDocumentSanitizer m_sanitizer;
|
||||
QPushButton* m_sanitizeButton;
|
||||
bool m_sanitizationInProgress;
|
||||
bool m_wasSanitized;
|
||||
QFuture<void> m_future;
|
||||
pdf::PDFDocument m_sanitizedDocument;
|
||||
SanitizationInfo m_sanitizationInfo;
|
||||
};
|
||||
|
||||
} // namespace pdfviewer
|
||||
|
||||
#endif // PDFSANITIZEDOCUMENTDIALOG_H
|
82
Pdf4QtViewer/pdfsanitizedocumentdialog.ui
Normal file
82
Pdf4QtViewer/pdfsanitizedocumentdialog.ui
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PDFSanitizeDocumentDialog</class>
|
||||
<widget class="QDialog" name="PDFSanitizeDocumentDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>741</width>
|
||||
<height>530</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Document sanitization</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="dialogLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="sanitizationSettingsGroupBox">
|
||||
<property name="title">
|
||||
<string>Sanitization Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="groupBoxLayout"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="logTextEdit">
|
||||
<property name="undoRedoEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>PDFSanitizeDocumentDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PDFSanitizeDocumentDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@@ -24,7 +24,6 @@
|
||||
#include "pdfviewersettingsdialog.h"
|
||||
#include "pdfdocumentpropertiesdialog.h"
|
||||
#include "pdfrendertoimagesdialog.h"
|
||||
#include "pdfoptimizedocumentdialog.h"
|
||||
#include "pdfdbgheap.h"
|
||||
|
||||
#include "pdfdocumentreader.h"
|
||||
@@ -161,6 +160,7 @@ PDFViewerMainWindow::PDFViewerMainWindow(QWidget* parent) :
|
||||
m_actionManager->setAction(PDFActionManager::SendByMail, ui->actionSend_by_E_Mail);
|
||||
m_actionManager->setAction(PDFActionManager::RenderToImages, ui->actionRender_to_Images);
|
||||
m_actionManager->setAction(PDFActionManager::Optimize, ui->actionOptimize);
|
||||
m_actionManager->setAction(PDFActionManager::Sanitize, ui->actionSanitize);
|
||||
m_actionManager->setAction(PDFActionManager::Encryption, ui->actionEncryption);
|
||||
m_actionManager->setAction(PDFActionManager::FitPage, ui->actionFitPage);
|
||||
m_actionManager->setAction(PDFActionManager::FitWidth, ui->actionFitWidth);
|
||||
|
@@ -20,7 +20,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>770</width>
|
||||
<height>37</height>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
@@ -142,6 +142,7 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEncryption"/>
|
||||
<addaction name="actionOptimize"/>
|
||||
<addaction name="actionSanitize"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuInsert">
|
||||
@@ -635,6 +636,9 @@
|
||||
<property name="text">
|
||||
<string>Optimize...</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Optimizes document to reduce file size.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave_As">
|
||||
<property name="icon">
|
||||
@@ -924,6 +928,14 @@
|
||||
<string>Certificates...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSanitize">
|
||||
<property name="text">
|
||||
<string>Sanitize...</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Sanitize document to remove sensitive information.</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
|
@@ -24,7 +24,6 @@
|
||||
#include "pdfviewersettingsdialog.h"
|
||||
#include "pdfdocumentpropertiesdialog.h"
|
||||
#include "pdfrendertoimagesdialog.h"
|
||||
#include "pdfoptimizedocumentdialog.h"
|
||||
#include "pdfdbgheap.h"
|
||||
|
||||
#include "pdfdocumentreader.h"
|
||||
|
Reference in New Issue
Block a user