diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts
index 0cf445a86..f4e08ce4d 100644
--- a/share/translations/keepassxc_en.ts
+++ b/share/translations/keepassxc_en.ts
@@ -7178,6 +7178,26 @@ Do you want to overwrite it?
Image format not supported
+
+ Failed to load the PDF
+
+
+
+ Failed to read the PDF: The file is locked
+
+
+
+ Failed to read the PDF: No pages found
+
+
+
+ Failed to read the PDF: Unable to create a page
+
+
+
+ Failed to render the PDF page
+
+
QMessageBox
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 84c6090ba..ccb96879f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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)
diff --git a/src/gui/entry/PreviewEntryAttachmentsDialog.cpp b/src/gui/entry/PreviewEntryAttachmentsDialog.cpp
index 1bc47c4e0..a5ab50fe7 100644
--- a/src/gui/entry/PreviewEntryAttachmentsDialog.cpp
+++ b/src/gui/entry/PreviewEntryAttachmentsDialog.cpp
@@ -24,6 +24,12 @@
#include
#include
+#include
+
+#include
+#include
+#include
+
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::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(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(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
diff --git a/src/gui/entry/PreviewEntryAttachmentsDialog.h b/src/gui/entry/PreviewEntryAttachmentsDialog.h
index 27fbe8d94..35454e1d7 100644
--- a/src/gui/entry/PreviewEntryAttachmentsDialog.h
+++ b/src/gui/entry/PreviewEntryAttachmentsDialog.h
@@ -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 m_ui;
diff --git a/tests/TestTools.cpp b/tests/TestTools.cpp
index 27a468929..95b73c86c 100644
--- a/tests/TestTools.cpp
+++ b/tests/TestTools.cpp
@@ -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);
}
diff --git a/vcpkg.json b/vcpkg.json
index baa40686c..c64679af0 100644
--- a/vcpkg.json
+++ b/vcpkg.json
@@ -76,6 +76,10 @@
{
"name": "zlib",
"version>=": "1.3"
+ },
+ {
+ "name": "poppler",
+ "version>=": "23.1.0"
}
]
-}
+}
\ No newline at end of file