From 6bf6c4e062bdb4df103a489406e6bca6c26704ef Mon Sep 17 00:00:00 2001 From: Tomaž Vajngerl Date: Tue, 17 Dec 2024 12:08:30 +0900 Subject: pdf: test hybrid mode (PDF attachment) with encryption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a test that checks the attached file is correctly added to the PDF stream when encryption is enabled. Also add support for reading of attachments to PDFium wrapper. The test saves a document as encrypted PDF with hybrid mode enabled, then opens the document in PDFium and saves the attachment content to a temporary file, which is opened again and the content checked. Change-Id: I918f67d269c31fe7826a49473e939d52bac7fe98 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178629 Reviewed-by: Miklos Vajna Tested-by: Jenkins CollaboraOffice Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178778 Reviewed-by: Tomaž Vajngerl Tested-by: Jenkins --- .../cppunit/pdfexport/data/SimpleTestDocument.fodt | 350 +++++++++++++++++++++ vcl/qa/cppunit/pdfexport/pdfexport2.cxx | 63 ++++ vcl/source/pdf/PDFiumLibrary.cxx | 56 ++++ 3 files changed, 469 insertions(+) create mode 100644 vcl/qa/cppunit/pdfexport/data/SimpleTestDocument.fodt (limited to 'vcl') diff --git a/vcl/qa/cppunit/pdfexport/data/SimpleTestDocument.fodt b/vcl/qa/cppunit/pdfexport/data/SimpleTestDocument.fodt new file mode 100644 index 000000000000..3978e97559fd --- /dev/null +++ b/vcl/qa/cppunit/pdfexport/data/SimpleTestDocument.fodt @@ -0,0 +1,350 @@ + + + + FirstName LastName2024-12-17T11:47:27.4622722012024-12-17T11:48:46.695838814FirstName LastNamePT1M20S1Collabora_OfficeDev/24.04.11.0$Linux_X86_64 LibreOffice_project/4203a38a1138d86389e402dbbd814f7a352cea2a + + + 0 + 0 + 34398 + 16198 + true + false + + + view2 + 12707 + 2501 + 0 + 0 + 34396 + 16196 + 0 + 1 + false + 140 + false + false + false + false + false + false + + + + + false + true + true + + false + 0 + false + true + true + false + false + 0 + true + false + false + false + false + false + true + false + false + true + false + true + true + false + true + false + false + false + false + true + true + false + false + false + false + false + false + high-resolution + false + 634772 + false + true + false + + + true + + false + false + false + true + true + true + false + 0 + true + false + false + true + true + true + false + true + false + false + false + false + false + true + true + false + false + + true + false + false + 0 + false + true + + false + false + true + true + false + false + true + 1 + false + false + false + false + false + false + false + true + false + false + false + true + false + + true + 670521 + true + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is a test document. + + + \ No newline at end of file diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx index 3445503f6377..f883a3c97540 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx @@ -6119,6 +6119,69 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf164106SplitReorderedClusters) fnCompareIndices(6, 13); } +CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPDFAttachmentsWithEncryptedFile) +{ + // Encrypt the document and use the hybrid mode. + // The original ODF document will be saved to the PDF as an attachment. + + aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + uno::Sequence aFilterData + = { comphelper::makePropertyValue("IsAddStream", true), + comphelper::makePropertyValue("EncryptFile", true), + comphelper::makePropertyValue("DocumentOpenPassword", OUString("secret")) }; + aMediaDescriptor["FilterData"] <<= aFilterData; + + saveAsPDF(u"SimpleTestDocument.fodt"); + + // Parse the round-tripped document with PDFium + auto pPdfDocument = parsePDFExport("secret"_ostr); + + // Should be 1 page + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + + // Should have 1 attachment + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getAttachmentCount()); + + // Get the attachment + auto pAttachment = pPdfDocument->getAttachment(0); + CPPUNIT_ASSERT(pAttachment); + + // Check the filename of the attachment + CPPUNIT_ASSERT_EQUAL(u"Original.odt"_ustr, pAttachment->getName()); + + // Write the attachment to the buffer + std::vector aBuffer; + CPPUNIT_ASSERT(pAttachment->getFile(aBuffer)); + CPPUNIT_ASSERT_GREATER(size_t(0), aBuffer.size()); + + // Create a temp file and store the content of the attachment + utl::TempFileNamed aTempFile; + aTempFile.EnableKillingFile(); + { + SvFileStream aOutputStream(aTempFile.GetURL(), StreamMode::WRITE | StreamMode::TRUNC); + aOutputStream.WriteBytes(aBuffer.data(), aBuffer.size()); + } + + // Load the attached document from the temp file + UnoApiTest::loadFromURL(aTempFile.GetURL()); + + // Check the content - first paragraph + uno::Reference xTextDocument(mxComponent, uno::UNO_QUERY); + CPPUNIT_ASSERT(xTextDocument.is()); + uno::Reference xParagraphEnumAccess(xTextDocument->getText(), + uno::UNO_QUERY); + CPPUNIT_ASSERT(xParagraphEnumAccess.is()); + uno::Reference xParagraphEnum + = xParagraphEnumAccess->createEnumeration(); + uno::Reference const xElement(xParagraphEnum->nextElement(), + uno::UNO_QUERY); + CPPUNIT_ASSERT(xElement.is()); + uno::Reference const xParagraph(xElement, uno::UNO_QUERY); + CPPUNIT_ASSERT(xParagraph.is()); + + CPPUNIT_ASSERT_EQUAL(u"This is a test document."_ustr, xParagraph->getString()); +} + } // end anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx index a1552e9ca1bf..3eea4a2010fc 100644 --- a/vcl/source/pdf/PDFiumLibrary.cxx +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -489,6 +490,23 @@ public: FPDF_FORMHANDLE getPointer(); }; +class PDFiumAttachmentImpl final : public PDFiumAttachment +{ +private: + FPDF_ATTACHMENT mpAttachment; + PDFiumAttachmentImpl(const PDFiumSignatureImpl&) = delete; + PDFiumAttachmentImpl& operator=(const PDFiumSignatureImpl&) = delete; + +public: + PDFiumAttachmentImpl(FPDF_ATTACHMENT pAttachment) + : mpAttachment(pAttachment) + { + } + + OUString getName() override; + bool getFile(std::vector& rOutBuffer) override; +}; + class PDFiumDocumentImpl : public PDFiumDocument { private: @@ -509,11 +527,13 @@ public: basegfx::B2DSize getPageSize(int nIndex) override; int getPageCount() override; int getSignatureCount() override; + int getAttachmentCount() override; int getFileVersion() override; bool saveWithVersion(SvMemoryStream& rStream, int nFileVersion) override; std::unique_ptr openPage(int nIndex) override; std::unique_ptr getSignature(int nIndex) override; + std::unique_ptr getAttachment(int nIndex) override; std::vector getTrailerEnds() override; OUString getBookmarks() override; }; @@ -742,6 +762,29 @@ util::DateTime PDFiumSignatureImpl::getTime() return aRet; } +OUString PDFiumAttachmentImpl::getName() +{ + return getUnicodeString([this](FPDF_WCHAR* buffer, unsigned long length) { + return FPDFAttachment_GetName(mpAttachment, buffer, length); + }); +} + +bool PDFiumAttachmentImpl::getFile(std::vector& rOutBuffer) +{ + rOutBuffer.clear(); + + unsigned long nLength{}; + if (!FPDFAttachment_GetFile(mpAttachment, nullptr, 0, &nLength)) + return false; + + rOutBuffer.resize(nLength); + unsigned long nActualLength{}; + if (!FPDFAttachment_GetFile(mpAttachment, rOutBuffer.data(), nLength, &nActualLength)) + return false; + rOutBuffer.resize(nActualLength); + return true; +} + PDFiumDocumentImpl::PDFiumDocumentImpl(FPDF_DOCUMENT pPdfDocument) : mpPdfDocument(pPdfDocument) , m_aFormCallbacks() @@ -782,6 +825,17 @@ std::unique_ptr PDFiumDocumentImpl::getSignature(int nIndex) return pPDFiumSignature; } +std::unique_ptr PDFiumDocumentImpl::getAttachment(int nIndex) +{ + std::unique_ptr pPDFiumAttachment; + FPDF_ATTACHMENT pAttachment = FPDFDoc_GetAttachment(mpPdfDocument, nIndex); + if (pAttachment) + { + pPDFiumAttachment = std::make_unique(pAttachment); + } + return pPDFiumAttachment; +} + std::vector PDFiumDocumentImpl::getTrailerEnds() { int nNumTrailers = FPDF_GetTrailerEnds(mpPdfDocument, nullptr, 0); @@ -855,6 +909,8 @@ int PDFiumDocumentImpl::getPageCount() { return FPDF_GetPageCount(mpPdfDocument) int PDFiumDocumentImpl::getSignatureCount() { return FPDF_GetSignatureCount(mpPdfDocument); } +int PDFiumDocumentImpl::getAttachmentCount() { return FPDFDoc_GetAttachmentCount(mpPdfDocument); } + int PDFiumDocumentImpl::getFileVersion() { int nFileVersion = 0; -- cgit