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>
|
||||
<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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,10 @@
|
|||
{
|
||||
"name": "zlib",
|
||||
"version>=": "1.3"
|
||||
},
|
||||
{
|
||||
"name": "poppler",
|
||||
"version>=": "23.1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue