mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-05 21:14:20 -04:00
Add PDF preview support in attachment dialog using Poppler
This commit is contained in:
parent
594cb7710c
commit
070ace6158
6 changed files with 111 additions and 20 deletions
|
@ -7178,6 +7178,26 @@ Do you want to overwrite it?</source>
|
||||||
<source>Image format not supported</source>
|
<source>Image format not supported</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to load the PDF</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to read the PDF: The file is locked</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to read the PDF: No pages found</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to read the PDF: Unable to create a page</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Failed to render the PDF page</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>QMessageBox</name>
|
<name>QMessageBox</name>
|
||||||
|
|
|
@ -388,6 +388,12 @@ target_link_libraries(keepassxc_gui
|
||||||
${keeshare_LIB}
|
${keeshare_LIB}
|
||||||
${sshagent_LIB})
|
${sshagent_LIB})
|
||||||
|
|
||||||
|
# Find Poppler
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(POPPLER_CPP REQUIRED IMPORTED_TARGET poppler-cpp)
|
||||||
|
|
||||||
|
target_link_libraries(keepassxc_gui PkgConfig::POPPLER_CPP)
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
target_link_libraries(keepassxc_gui "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication -framework ScreenCaptureKit")
|
target_link_libraries(keepassxc_gui "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication -framework ScreenCaptureKit")
|
||||||
if(Qt5MacExtras_FOUND)
|
if(Qt5MacExtras_FOUND)
|
||||||
|
|
|
@ -24,6 +24,12 @@
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <poppler-document.h>
|
||||||
|
#include <poppler-page-renderer.h>
|
||||||
|
#include <poppler-page.h>
|
||||||
|
|
||||||
PreviewEntryAttachmentsDialog::PreviewEntryAttachmentsDialog(QWidget* parent)
|
PreviewEntryAttachmentsDialog::PreviewEntryAttachmentsDialog(QWidget* parent)
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
, m_ui(new Ui::EntryAttachmentsDialog)
|
, m_ui(new Ui::EntryAttachmentsDialog)
|
||||||
|
@ -84,7 +90,58 @@ void PreviewEntryAttachmentsDialog::update()
|
||||||
void PreviewEntryAttachmentsDialog::updatePdfAttachment(const QByteArray& data)
|
void PreviewEntryAttachmentsDialog::updatePdfAttachment(const QByteArray& data)
|
||||||
{
|
{
|
||||||
// To preview a PDF as an image, you need to install the qt5-image-formats-plugin-pdf
|
// To preview a PDF as an image, you need to install the qt5-image-formats-plugin-pdf
|
||||||
updateImageAttachment(data);
|
poppler::byte_array array{std::cbegin(data), std::cend(data)};
|
||||||
|
|
||||||
|
auto doc = std::unique_ptr<poppler::document>(poppler::document::load_from_data(&array));
|
||||||
|
if (!doc) {
|
||||||
|
updateTextAttachment(tr("Failed to load the PDF").toUtf8());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locked PDF files are not supported
|
||||||
|
if (doc->is_locked()) {
|
||||||
|
updateTextAttachment(tr("Failed to read the PDF: The file is locked").toUtf8());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!doc->pages()) {
|
||||||
|
updateTextAttachment(tr("Failed to read the PDF: No pages found").toUtf8());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preview the first page of the document.
|
||||||
|
auto page = std::unique_ptr<poppler::page>(doc->create_page(0));
|
||||||
|
if (!page) {
|
||||||
|
updateTextAttachment(tr("Failed to read the PDF: Unable to create a page").toUtf8());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
poppler::page_renderer renderer{};
|
||||||
|
auto popplerImage = renderer.render_page(page.get());
|
||||||
|
|
||||||
|
QImage image(reinterpret_cast<const uchar*>(popplerImage.const_data()),
|
||||||
|
popplerImage.width(),
|
||||||
|
popplerImage.height(),
|
||||||
|
popplerImage.bytes_per_row(),
|
||||||
|
QImage::Format_ARGB32);
|
||||||
|
|
||||||
|
if (image.isNull()) {
|
||||||
|
updateTextAttachment(tr("Failed to render the PDF page").toUtf8());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateImageAttachment(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize PreviewEntryAttachmentsDialog::calcucateImageSize()
|
||||||
|
{
|
||||||
|
// Scale the image to the contents rect minus another set of margins to avoid scrollbars
|
||||||
|
auto margins = m_ui->attachmentTextEdit->contentsMargins();
|
||||||
|
auto size = m_ui->attachmentTextEdit->contentsRect().size();
|
||||||
|
size.setWidth(size.width() - margins.left() - margins.right());
|
||||||
|
size.setHeight(size.height() - margins.top() - margins.bottom());
|
||||||
|
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PreviewEntryAttachmentsDialog::updateTextAttachment(const QByteArray& data)
|
void PreviewEntryAttachmentsDialog::updateTextAttachment(const QByteArray& data)
|
||||||
|
@ -100,17 +157,15 @@ void PreviewEntryAttachmentsDialog::updateImageAttachment(const QByteArray& data
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateImageAttachment(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreviewEntryAttachmentsDialog::updateImageAttachment(const QImage& image)
|
||||||
|
{
|
||||||
m_ui->attachmentTextEdit->clear();
|
m_ui->attachmentTextEdit->clear();
|
||||||
auto cursor = m_ui->attachmentTextEdit->textCursor();
|
auto cursor = m_ui->attachmentTextEdit->textCursor();
|
||||||
|
|
||||||
// Scale the image to the contents rect minus another set of margins to avoid scrollbars
|
cursor.insertImage(image.scaled(calcucateImageSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||||||
auto margins = m_ui->attachmentTextEdit->contentsMargins();
|
|
||||||
auto size = m_ui->attachmentTextEdit->contentsRect().size();
|
|
||||||
size.setWidth(size.width() - margins.left() - margins.right());
|
|
||||||
size.setHeight(size.height() - margins.top() - margins.bottom());
|
|
||||||
image = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
|
|
||||||
cursor.insertImage(image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tools::MimeType PreviewEntryAttachmentsDialog::attachmentType(const QByteArray& data) const
|
Tools::MimeType PreviewEntryAttachmentsDialog::attachmentType(const QByteArray& data) const
|
||||||
|
|
|
@ -51,6 +51,9 @@ private:
|
||||||
void updateTextAttachment(const QByteArray& data);
|
void updateTextAttachment(const QByteArray& data);
|
||||||
void updateImageAttachment(const QByteArray& data);
|
void updateImageAttachment(const QByteArray& data);
|
||||||
void updatePdfAttachment(const QByteArray& data);
|
void updatePdfAttachment(const QByteArray& data);
|
||||||
|
void updateImageAttachment(const QImage& image);
|
||||||
|
|
||||||
|
QSize calcucateImageSize();
|
||||||
|
|
||||||
QScopedPointer<Ui::EntryAttachmentsDialog> m_ui;
|
QScopedPointer<Ui::EntryAttachmentsDialog> m_ui;
|
||||||
|
|
||||||
|
|
|
@ -301,10 +301,13 @@ void TestTools::testMimeTypes()
|
||||||
"image/svg+xml" // SVG images
|
"image/svg+xml" // SVG images
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const QStringList pdfMimeType = {
|
||||||
|
"application/pdf", // PDF documents
|
||||||
|
};
|
||||||
|
|
||||||
const QStringList UnknownMimeTypes = {
|
const QStringList UnknownMimeTypes = {
|
||||||
"audio/mpeg", // MPEG audio files
|
"audio/mpeg", // MPEG audio files
|
||||||
"video/mp4", // MP4 video files
|
"video/mp4", // MP4 video files
|
||||||
"application/pdf", // PDF documents
|
|
||||||
"application/zip", // ZIP archives
|
"application/zip", // ZIP archives
|
||||||
"application/x-tar", // TAR archives
|
"application/x-tar", // TAR archives
|
||||||
"application/x-rar-compressed", // RAR archives
|
"application/x-rar-compressed", // RAR archives
|
||||||
|
@ -327,15 +330,15 @@ void TestTools::testMimeTypes()
|
||||||
"application/x-shellscript", // Shell scripts
|
"application/x-shellscript", // Shell scripts
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& mime : TextMimeTypes) {
|
auto compare = [](const QStringList& mimeList, Tools::MimeType exptectedType) {
|
||||||
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::PlainText);
|
for (const auto& mime : mimeList) {
|
||||||
}
|
QCOMPARE(Tools::toMimeType(mime), exptectedType);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (const auto& mime : ImageMimeTypes) {
|
compare(TextMimeTypes, Tools::MimeType::PlainText);
|
||||||
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::Image);
|
compare(ImageMimeTypes, Tools::MimeType::Image);
|
||||||
}
|
compare(pdfMimeType, Tools::MimeType::Pdf);
|
||||||
|
|
||||||
for (const auto& mime : UnknownMimeTypes) {
|
compare(UnknownMimeTypes, Tools::MimeType::Unknown);
|
||||||
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::Unknown);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,10 @@
|
||||||
{
|
{
|
||||||
"name": "zlib",
|
"name": "zlib",
|
||||||
"version>=": "1.3"
|
"version>=": "1.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "poppler",
|
||||||
|
"version>=": "23.1.0"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue