diff options
author | Miklos Vajna <vmiklos@collabora.com> | 2022-07-20 08:16:57 +0200 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.com> | 2022-07-20 12:54:45 +0200 |
commit | 7d56dae3375dc0180aa6d20983b3f5f962302588 (patch) | |
tree | 6068be7826ac24c4f5519545829f8db698a373d8 /vcl | |
parent | 845393b9aa9057c5ace3c41ad5ac629b895d7f7f (diff) |
tdf#127236 vcl: fix missing encryption of PDF images during export
Regression from commit 78e25558e86188314b9b72048b8ddca18697cb86
(tdf#106059 PDF export: create a reference XObject for JPG images with
PDF data, 2017-02-23), once a PDF image was inserted to a document, an
encrypted PDF export lost those images.
The reason for this is that we started to preserve PDF images as vector
data with the above commit, but this means we copied over PDF objects
from PDF images to the export result as-is, so encryption was not
performed for them.
Fix this by separating the write of the PDF object headers, stream
content and object footer and then calling
checkAndEnableStreamEncryption() / disableStreamEncryption() for each
object, even if it's not something our PDF export created but comes from
a PDF image.
Note that when existing PDF files are signed, PDF objects are also
copied into a vcl::filter::PDFDocument, but such PDF images are never
encrypted, so it's fine to have stub implementations in
vcl::filter::PDFDocument.
Change-Id: I2f74b9f51cd35b4319221532ca890e197bab9cf3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137242
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/inc/pdf/pdfwriter_impl.hxx | 4 | ||||
-rw-r--r-- | vcl/qa/cppunit/PDFiumLibraryTest.cxx | 18 | ||||
-rw-r--r-- | vcl/qa/cppunit/filter/ipdf/ipdf.cxx | 2 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/data/rectangles.pdf | 54 | ||||
-rw-r--r-- | vcl/qa/cppunit/pdfexport/pdfexport.cxx | 50 | ||||
-rw-r--r-- | vcl/source/filter/ipdf/pdfread.cxx | 8 | ||||
-rw-r--r-- | vcl/source/gdi/pdfobjectcopier.cxx | 29 | ||||
-rw-r--r-- | vcl/source/gdi/pdfwriter_impl.cxx | 32 | ||||
-rw-r--r-- | vcl/source/graphic/VectorGraphicSearch.cxx | 3 | ||||
-rw-r--r-- | vcl/source/pdf/PDFiumLibrary.cxx | 13 |
10 files changed, 186 insertions, 27 deletions
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx index 228df387fb6d..d718529476b9 100644 --- a/vcl/inc/pdf/pdfwriter_impl.hxx +++ b/vcl/inc/pdf/pdfwriter_impl.hxx @@ -807,9 +807,9 @@ i12626 void addRoleMap(OString aAlias, PDFWriter::StructElement eType); /* this function implements part of the PDF spec algorithm 3.1 in encryption, the rest (the actual encryption) is in PDFWriterImpl::writeBuffer */ - void checkAndEnableStreamEncryption( sal_Int32 nObject ); + void checkAndEnableStreamEncryption( sal_Int32 nObject ) override; - void disableStreamEncryption() { m_bEncryptThisStream = false; }; + void disableStreamEncryption() override { m_bEncryptThisStream = false; }; /* */ void enableStringEncryption( sal_Int32 nObject ); diff --git a/vcl/qa/cppunit/PDFiumLibraryTest.cxx b/vcl/qa/cppunit/PDFiumLibraryTest.cxx index 9ba40c438f50..1f82f24acccf 100644 --- a/vcl/qa/cppunit/PDFiumLibraryTest.cxx +++ b/vcl/qa/cppunit/PDFiumLibraryTest.cxx @@ -70,7 +70,8 @@ void PDFiumLibraryTest::testDocument() auto pPdfium = vcl::pdf::PDFiumLibrary::get(); CPPUNIT_ASSERT(pPdfium); - auto pDocument = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize()); + auto pDocument + = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize(), OString()); CPPUNIT_ASSERT(pDocument); CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); @@ -95,7 +96,8 @@ void PDFiumLibraryTest::testPages() auto& rDataContainer = pVectorGraphicData->getBinaryDataContainer(); auto pPdfium = vcl::pdf::PDFiumLibrary::get(); - auto pDocument = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize()); + auto pDocument + = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize(), OString()); CPPUNIT_ASSERT(pDocument); CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); @@ -119,7 +121,8 @@ void PDFiumLibraryTest::testPageObjects() auto& rDataContainer = pVectorGraphicData->getBinaryDataContainer(); auto pPdfium = vcl::pdf::PDFiumLibrary::get(); - auto pDocument = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize()); + auto pDocument + = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize(), OString()); CPPUNIT_ASSERT(pDocument); CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); @@ -171,7 +174,8 @@ void PDFiumLibraryTest::testAnnotationsMadeInEvince() auto& rDataContainer = pVectorGraphicData->getBinaryDataContainer(); auto pPdfium = vcl::pdf::PDFiumLibrary::get(); - auto pDocument = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize()); + auto pDocument + = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize(), OString()); CPPUNIT_ASSERT(pDocument); CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); @@ -226,7 +230,8 @@ void PDFiumLibraryTest::testAnnotationsMadeInAcrobat() auto& rDataContainer = pVectorGraphicData->getBinaryDataContainer(); auto pPdfium = vcl::pdf::PDFiumLibrary::get(); - auto pDocument = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize()); + auto pDocument + = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize(), OString()); CPPUNIT_ASSERT(pDocument); CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); @@ -337,7 +342,8 @@ void PDFiumLibraryTest::testAnnotationsDifferentTypes() auto& rDataContainer = pVectorGraphicData->getBinaryDataContainer(); auto pPdfium = vcl::pdf::PDFiumLibrary::get(); - auto pDocument = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize()); + auto pDocument + = pPdfium->openDocument(rDataContainer.getData(), rDataContainer.getSize(), OString()); CPPUNIT_ASSERT(pDocument); CPPUNIT_ASSERT_EQUAL(1, pDocument->getPageCount()); diff --git a/vcl/qa/cppunit/filter/ipdf/ipdf.cxx b/vcl/qa/cppunit/filter/ipdf/ipdf.cxx index 30fbccc7d2d9..620b892fdba8 100644 --- a/vcl/qa/cppunit/filter/ipdf/ipdf.cxx +++ b/vcl/qa/cppunit/filter/ipdf/ipdf.cxx @@ -133,7 +133,7 @@ CPPUNIT_TEST_FIXTURE(VclFilterIpdfTest, testPDFAddVisibleSignatureLastPage) aMemory.WriteStream(aFile); // Last page. std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument - = pPDFium->openDocument(aMemory.GetData(), aMemory.GetSize()); + = pPDFium->openDocument(aMemory.GetData(), aMemory.GetSize(), OString()); std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/1); // Without the accompanying fix in place, this test would have failed with: // - Expected: 1 diff --git a/vcl/qa/cppunit/pdfexport/data/rectangles.pdf b/vcl/qa/cppunit/pdfexport/data/rectangles.pdf new file mode 100644 index 000000000000..6911d229aa06 --- /dev/null +++ b/vcl/qa/cppunit/pdfexport/data/rectangles.pdf @@ -0,0 +1,54 @@ +%PDF-1.7 +% +1 0 obj << + /Type /Catalog + /Pages 2 0 R +>> +endobj +2 0 obj << + /Type /Pages + /MediaBox [0 0 200 300] + /Count 1 + /Kids [3 0 R] +>> +endobj +3 0 obj << + /Type /Page + /Parent 2 0 R + /Contents 4 0 R +>> +endobj +4 0 obj << + /Length 188 +>> +stream +q +0 0 0 rg +0 290 10 10 re B* +10 150 50 30 re B* +0 0 1 rg +190 290 10 10 re B* +70 232 50 30 re B* +0 1 0 rg +190 0 10 10 re B* +130 150 50 30 re B* +1 0 0 rg +0 0 10 10 re B* +70 67 50 30 re B* +Q +endstream +endobj +xref +0 5 +0000000000 65535 f +0000000015 00000 n +0000000068 00000 n +0000000157 00000 n +0000000226 00000 n +trailer << + /Root 1 0 R + /Size 5 +>> +startxref +466 +%%EOF diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx index 2531dab1a3f4..d77cc4a4b00d 100644 --- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx +++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx @@ -68,7 +68,7 @@ protected: utl::TempFile maTempFile; SvMemoryStream maMemory; utl::MediaDescriptor aMediaDescriptor; - std::unique_ptr<vcl::pdf::PDFiumDocument> parseExport(); + std::unique_ptr<vcl::pdf::PDFiumDocument> parseExport(const OString& rPassword = OString()); std::shared_ptr<vcl::pdf::PDFium> mpPDFium; public: @@ -81,13 +81,13 @@ public: PdfExportTest::PdfExportTest() { maTempFile.EnableKillingFile(); } -std::unique_ptr<vcl::pdf::PDFiumDocument> PdfExportTest::parseExport() +std::unique_ptr<vcl::pdf::PDFiumDocument> PdfExportTest::parseExport(const OString& rPassword) { SvFileStream aFile(maTempFile.GetURL(), StreamMode::READ); maMemory.WriteStream(aFile); std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument - = pPDFium->openDocument(maMemory.GetData(), maMemory.GetSize()); + = pPDFium->openDocument(maMemory.GetData(), maMemory.GetSize(), rPassword); CPPUNIT_ASSERT(pPdfDocument); return pPdfDocument; } @@ -3407,6 +3407,50 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageAnnots) // i.e. not only the hyperlink but also the 2 comments were exported, leading to duplication. CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount()); } + +CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageEncryption) +{ + // Given an empty document, with an inserted PDF image: + mxComponent = loadFromDesktop("private:factory/swriter"); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor(); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xGraphicObject( + xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY); + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "rectangles.pdf"; + xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL)); + uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY); + xShape->setSize(awt::Size(1000, 1000)); + uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY); + xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false); + + // When saving as encrypted PDF: + uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY); + aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export"); + uno::Sequence<beans::PropertyValue> aFilterData = { + comphelper::makePropertyValue("EncryptFile", true), + comphelper::makePropertyValue("DocumentOpenPassword", OUString("secret")), + }; + aMediaDescriptor["FilterData"] <<= aFilterData; + xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList()); + + // Then make sure that the image is not lost: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parseExport("secret"); + CPPUNIT_ASSERT(pPdfDocument); + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount()); + std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0); + CPPUNIT_ASSERT(pPdfPage); + CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0); + CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType()); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 2 + // - Actual : 0 + // i.e. instead of the white background and the actual form child, the image was lost due to + // missing encryption. + CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount()); +} } // end anonymous namespace CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx index 112aa411a67d..8510b0a0c207 100644 --- a/vcl/source/filter/ipdf/pdfread.cxx +++ b/vcl/source/filter/ipdf/pdfread.cxx @@ -79,7 +79,7 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream) { // Load the buffer using pdfium. std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument - = pPdfium->openDocument(aInBuffer.GetData(), aInBuffer.GetSize()); + = pPdfium->openDocument(aInBuffer.GetData(), aInBuffer.GetSize(), OString()); if (!pPdfDocument) return false; @@ -128,7 +128,8 @@ size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<BitmapEx>& r } // Load the buffer using pdfium. - std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = pPdfium->openDocument(pBuffer, nSize); + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPdfium->openDocument(pBuffer, nSize, OString()); if (!pPdfDocument) return 0; @@ -441,7 +442,8 @@ size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& rG } // Load the buffer using pdfium. - auto pPdfDocument = pPdfium->openDocument(pGfxLink->GetData(), pGfxLink->GetDataSize()); + auto pPdfDocument + = pPdfium->openDocument(pGfxLink->GetData(), pGfxLink->GetDataSize(), OString()); if (!pPdfDocument) return 0; diff --git a/vcl/source/gdi/pdfobjectcopier.cxx b/vcl/source/gdi/pdfobjectcopier.cxx index 1cb6c209d699..93b7b4989710 100644 --- a/vcl/source/gdi/pdfobjectcopier.cxx +++ b/vcl/source/gdi/pdfobjectcopier.cxx @@ -113,12 +113,10 @@ sal_Int32 PDFObjectCopier::copyExternalResource(SvMemoryStream& rDocBuffer, aLine.append(" >>\n"); } - if (filter::PDFStreamElement* pStream = rObject.GetStream()) + filter::PDFStreamElement* pStream = rObject.GetStream(); + if (pStream) { aLine.append("stream\n"); - SvMemoryStream& rStream = pStream->GetMemory(); - aLine.append(static_cast<const char*>(rStream.GetData()), rStream.GetSize()); - aLine.append("\nendstream\n"); } if (filter::PDFArrayElement* pArray = rObject.GetArray()) @@ -146,13 +144,32 @@ sal_Int32 PDFObjectCopier::copyExternalResource(SvMemoryStream& rDocBuffer, aLine.append("\n"); } - aLine.append("endobj\n\n"); - // We have the whole object, now write it to the output. if (!m_rContainer.updateObject(nObject)) return -1; if (!m_rContainer.writeBuffer(aLine.getStr(), aLine.getLength())) return -1; + aLine.setLength(0); + + if (pStream) + { + SvMemoryStream& rStream = pStream->GetMemory(); + m_rContainer.checkAndEnableStreamEncryption(nObject); + aLine.append(static_cast<const char*>(rStream.GetData()), rStream.GetSize()); + if (!m_rContainer.writeBuffer(aLine.getStr(), aLine.getLength())) + return -1; + aLine.setLength(0); + m_rContainer.disableStreamEncryption(); + + aLine.append("\nendstream\n"); + if (!m_rContainer.writeBuffer(aLine.getStr(), aLine.getLength())) + return -1; + aLine.setLength(0); + } + + aLine.append("endobj\n\n"); + if (!m_rContainer.writeBuffer(aLine.getStr(), aLine.getLength())) + return -1; return nObject; } diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index ae78eda251e5..f16bdc090f03 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -8659,16 +8659,34 @@ void PDFWriterImpl::writeReferenceXObject(const ReferenceXObjectEmit& rEmit) aLine.append(nLength); aLine.append(">>\nstream\n"); + if (g_bDebugDisableCompression) + { + emitComment("PDFWriterImpl::writeReferenceXObject, WrappedFormObject"); + } + if (!updateObject(nWrappedFormObject)) + return; + if (!writeBuffer(aLine.getStr(), aLine.getLength())) + return; + aLine.setLength(0); + + checkAndEnableStreamEncryption(nWrappedFormObject); // Copy the original page streams to the form XObject stream. aLine.append(static_cast<const char*>(aStream.GetData()), aStream.GetSize()); - aLine.append("\nendstream\nendobj\n\n"); - if (!updateObject(nWrappedFormObject)) + if (!writeBuffer(aLine.getStr(), aLine.getLength())) return; + aLine.setLength(0); + disableStreamEncryption(); + + aLine.append("\nendstream\nendobj\n\n"); if (!writeBuffer(aLine.getStr(), aLine.getLength())) return; } OStringBuffer aLine; + if (g_bDebugDisableCompression) + { + emitComment("PDFWriterImpl::writeReferenceXObject, FormObject"); + } if (!updateObject(rEmit.m_nFormObject)) return; @@ -8747,7 +8765,17 @@ void PDFWriterImpl::writeReferenceXObject(const ReferenceXObjectEmit& rEmit) aLine.append(aStream.getLength()); aLine.append(">>\nstream\n"); + if (!writeBuffer(aLine.getStr(), aLine.getLength())) + return; + aLine.setLength(0); + + checkAndEnableStreamEncryption(rEmit.m_nFormObject); aLine.append(aStream.getStr()); + if (!writeBuffer(aLine.getStr(), aLine.getLength())) + return; + aLine.setLength(0); + disableStreamEncryption(); + aLine.append("\nendstream\nendobj\n\n"); CHECK_RETURN2(writeBuffer(aLine.getStr(), aLine.getLength())); } diff --git a/vcl/source/graphic/VectorGraphicSearch.cxx b/vcl/source/graphic/VectorGraphicSearch.cxx index 6d26fd861fd7..69e69741ab42 100644 --- a/vcl/source/graphic/VectorGraphicSearch.cxx +++ b/vcl/source/graphic/VectorGraphicSearch.cxx @@ -232,7 +232,8 @@ bool VectorGraphicSearch::searchPDF(std::shared_ptr<VectorGraphicData> const& rD } mpImplementation->mpPdfDocument = mpImplementation->mpPDFium->openDocument( - rData->getBinaryDataContainer().getData(), rData->getBinaryDataContainer().getSize()); + rData->getBinaryDataContainer().getData(), rData->getBinaryDataContainer().getSize(), + OString()); if (!mpImplementation->mpPdfDocument) { diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx index d02e2a0bab49..b02358d43a9c 100644 --- a/vcl/source/pdf/PDFiumLibrary.cxx +++ b/vcl/source/pdf/PDFiumLibrary.cxx @@ -425,7 +425,8 @@ public: const OUString& getLastError() const override { return maLastError; } - std::unique_ptr<PDFiumDocument> openDocument(const void* pData, int nSize) override; + std::unique_ptr<PDFiumDocument> openDocument(const void* pData, int nSize, + const OString& rPassword) override; PDFErrorType getLastErrorCode() override; std::unique_ptr<PDFiumBitmap> createBitmap(int nWidth, int nHeight, int nAlpha) override; }; @@ -443,12 +444,18 @@ PDFiumImpl::PDFiumImpl() PDFiumImpl::~PDFiumImpl() { FPDF_DestroyLibrary(); } -std::unique_ptr<PDFiumDocument> PDFiumImpl::openDocument(const void* pData, int nSize) +std::unique_ptr<PDFiumDocument> PDFiumImpl::openDocument(const void* pData, int nSize, + const OString& rPassword) { maLastError = OUString(); std::unique_ptr<PDFiumDocument> pPDFiumDocument; - FPDF_DOCUMENT pDocument = FPDF_LoadMemDocument(pData, nSize, /*password=*/nullptr); + FPDF_BYTESTRING pPassword = nullptr; + if (!rPassword.isEmpty()) + { + pPassword = rPassword.getStr(); + } + FPDF_DOCUMENT pDocument = FPDF_LoadMemDocument(pData, nSize, pPassword); if (!pDocument) { |