Add PDF preview support in attachment dialog using Poppler

This commit is contained in:
w15dev 2025-01-19 02:43:10 +03:00 committed by Jonathan White
parent 594cb7710c
commit 070ace6158
No known key found for this signature in database
GPG key ID: 440FC65F2E0C6E01
6 changed files with 111 additions and 20 deletions

View file

@ -7178,6 +7178,26 @@ Do you want to overwrite it?</source>
<source>Image format not supported</source>
<translation type="unfinished"></translation>
</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>
<name>QMessageBox</name>

View file

@ -388,6 +388,12 @@ target_link_libraries(keepassxc_gui
${keeshare_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)
target_link_libraries(keepassxc_gui "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication -framework ScreenCaptureKit")
if(Qt5MacExtras_FOUND)

View file

@ -24,6 +24,12 @@
#include <QTextCursor>
#include <QtDebug>
#include <memory>
#include <poppler-document.h>
#include <poppler-page-renderer.h>
#include <poppler-page.h>
PreviewEntryAttachmentsDialog::PreviewEntryAttachmentsDialog(QWidget* parent)
: QDialog(parent)
, m_ui(new Ui::EntryAttachmentsDialog)
@ -84,7 +90,58 @@ void PreviewEntryAttachmentsDialog::update()
void PreviewEntryAttachmentsDialog::updatePdfAttachment(const QByteArray& data)
{
// 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)
@ -100,17 +157,15 @@ void PreviewEntryAttachmentsDialog::updateImageAttachment(const QByteArray& data
return;
}
updateImageAttachment(image);
}
void PreviewEntryAttachmentsDialog::updateImageAttachment(const QImage& image)
{
m_ui->attachmentTextEdit->clear();
auto cursor = m_ui->attachmentTextEdit->textCursor();
// 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());
image = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
cursor.insertImage(image);
cursor.insertImage(image.scaled(calcucateImageSize(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
Tools::MimeType PreviewEntryAttachmentsDialog::attachmentType(const QByteArray& data) const

View file

@ -51,6 +51,9 @@ private:
void updateTextAttachment(const QByteArray& data);
void updateImageAttachment(const QByteArray& data);
void updatePdfAttachment(const QByteArray& data);
void updateImageAttachment(const QImage& image);
QSize calcucateImageSize();
QScopedPointer<Ui::EntryAttachmentsDialog> m_ui;

View file

@ -301,10 +301,13 @@ void TestTools::testMimeTypes()
"image/svg+xml" // SVG images
};
const QStringList pdfMimeType = {
"application/pdf", // PDF documents
};
const QStringList UnknownMimeTypes = {
"audio/mpeg", // MPEG audio files
"video/mp4", // MP4 video files
"application/pdf", // PDF documents
"application/zip", // ZIP archives
"application/x-tar", // TAR archives
"application/x-rar-compressed", // RAR archives
@ -327,15 +330,15 @@ void TestTools::testMimeTypes()
"application/x-shellscript", // Shell scripts
};
for (const auto& mime : TextMimeTypes) {
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::PlainText);
auto compare = [](const QStringList& mimeList, Tools::MimeType exptectedType) {
for (const auto& mime : mimeList) {
QCOMPARE(Tools::toMimeType(mime), exptectedType);
}
};
for (const auto& mime : ImageMimeTypes) {
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::Image);
}
compare(TextMimeTypes, Tools::MimeType::PlainText);
compare(ImageMimeTypes, Tools::MimeType::Image);
compare(pdfMimeType, Tools::MimeType::Pdf);
for (const auto& mime : UnknownMimeTypes) {
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::Unknown);
}
compare(UnknownMimeTypes, Tools::MimeType::Unknown);
}

View file

@ -76,6 +76,10 @@
{
"name": "zlib",
"version>=": "1.3"
},
{
"name": "poppler",
"version>=": "23.1.0"
}
]
}