[Libreoffice-commits] core.git: Branch 'feature/cib_contract138c' - 6 commits - include/sfx2 include/vcl vcl/Library_vcl.mk vcl/qa vcl/source xmlsecurity/inc xmlsecurity/Library_xmlsecurity.mk xmlsecurity/qa xmlsecurity/source xmlsecurity/uiconfig xmlsecurity/workben

Miklos Vajna (via logerrit) logerrit at kemper.freedesktop.org
Tue Apr 6 12:37:29 UTC 2021


 include/sfx2/strings.hrc                                                   |    4 
 include/vcl/filter/PDFiumLibrary.hxx                                       |   93 ++++
 include/vcl/filter/pdfdocument.hxx                                         |    2 
 vcl/Library_vcl.mk                                                         |    1 
 vcl/qa/cppunit/pdfexport/pdfexport.cxx                                     |   12 
 vcl/source/filter/ipdf/pdfdocument.cxx                                     |   13 
 vcl/source/filter/ipdf/pdfread.cxx                                         |   20 
 vcl/source/pdf/PDFiumLibrary.cxx                                           |   94 ++++
 xmlsecurity/Library_xmlsecurity.mk                                         |    5 
 xmlsecurity/inc/pdfio/pdfdocument.hxx                                      |    6 
 xmlsecurity/qa/unit/pdfsigning/data/partial-in-between.pdf                 |binary
 xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx                              |   19 
 xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf |binary
 xmlsecurity/qa/unit/signing/signing.cxx                                    |   19 
 xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx                     |   26 -
 xmlsecurity/source/helper/pdfsignaturehelper.cxx                           |    3 
 xmlsecurity/source/pdfio/pdfdocument.cxx                                   |  209 ++++++++--
 xmlsecurity/uiconfig/ui/digitalsignaturesdialog.ui                         |    4 
 xmlsecurity/workben/pdfverify.cxx                                          |   11 
 19 files changed, 472 insertions(+), 69 deletions(-)

New commits:
commit 786afb9a1c03492e8327a3deb0ff997551a631d1
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Sep 4 17:17:48 2020 +0200
Commit:     Vasily Melenchuk <vasily.melenchuk at cib.de>
CommitDate: Tue Apr 6 15:06:10 2021 +0300

    xmlsecurity: pdf incremental updates that are non-commenting are invalid
    
    I.e. it's OK to add incremental updates for annotation/commenting
    purposes and that doesn't invalite existing signatures. Everything else
    does.
    
    (cherry picked from commit 61834cd574568613f0b0a2ee099a60fa5a8d9804)
    
    Conflicts:
            include/vcl/filter/PDFiumLibrary.hxx
            vcl/source/pdf/PDFiumLibrary.cxx
            xmlsecurity/qa/unit/signing/signing.cxx
    
    Change-Id: I4607c242b3c6f6b01517b02407e9e7a095e2e069

diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx
index b9bceabb8acf..ffc70874c19b 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -17,11 +17,16 @@
 #include <memory>
 #include <rtl/instance.hxx>
 #include <vcl/dllapi.h>
+#include <vcl/checksum.hxx>
+
+#include <fpdf_doc.h>
 
 namespace vcl
 {
 namespace pdf
 {
+class PDFiumDocument;
+
 class VCL_DLLPUBLIC PDFium final
 {
 private:
@@ -33,6 +38,49 @@ public:
     ~PDFium();
 };
 
+class VCL_DLLPUBLIC PDFiumPage final
+{
+private:
+    FPDF_PAGE mpPage;
+
+private:
+    PDFiumPage(const PDFiumPage&) = delete;
+    PDFiumPage& operator=(const PDFiumPage&) = delete;
+
+public:
+    PDFiumPage(FPDF_PAGE pPage)
+        : mpPage(pPage)
+    {
+    }
+
+    ~PDFiumPage()
+    {
+        if (mpPage)
+            FPDF_ClosePage(mpPage);
+    }
+
+    /// Get bitmap checksum of the page, without annotations/commenting.
+    BitmapChecksum getChecksum();
+};
+
+class VCL_DLLPUBLIC PDFiumDocument final
+{
+private:
+    FPDF_DOCUMENT mpPdfDocument;
+
+private:
+    PDFiumDocument(const PDFiumDocument&) = delete;
+    PDFiumDocument& operator=(const PDFiumDocument&) = delete;
+
+public:
+    PDFiumDocument(FPDF_DOCUMENT pPdfDocument);
+    ~PDFiumDocument();
+
+    int getPageCount();
+
+    std::unique_ptr<PDFiumPage> openPage(int nIndex);
+};
+
 struct PDFiumLibrary : public rtl::StaticWithInit<std::shared_ptr<PDFium>, PDFiumLibrary>
 {
     std::shared_ptr<PDFium> operator()() { return std::make_shared<PDFium>(); }
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index 5082c2567cce..330c473ac6dc 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -15,6 +15,10 @@
 #include <vcl/filter/PDFiumLibrary.hxx>
 #include <fpdf_doc.h>
 
+#include <vcl/bitmap.hxx>
+
+#include <bitmapwriteaccess.hxx>
+
 namespace vcl
 {
 namespace pdf
@@ -30,6 +34,58 @@ PDFium::PDFium()
 }
 
 PDFium::~PDFium() { FPDF_DestroyLibrary(); }
+
+PDFiumDocument::PDFiumDocument(FPDF_DOCUMENT pPdfDocument)
+    : mpPdfDocument(pPdfDocument)
+{
+}
+
+PDFiumDocument::~PDFiumDocument()
+{
+    if (mpPdfDocument)
+        FPDF_CloseDocument(mpPdfDocument);
+}
+
+std::unique_ptr<PDFiumPage> PDFiumDocument::openPage(int nIndex)
+{
+    std::unique_ptr<PDFiumPage> pPDFiumPage;
+    FPDF_PAGE pPage = FPDF_LoadPage(mpPdfDocument, nIndex);
+    if (pPage)
+    {
+        pPDFiumPage = std::make_unique<PDFiumPage>(pPage);
+    }
+    return pPDFiumPage;
+}
+
+int PDFiumDocument::getPageCount() { return FPDF_GetPageCount(mpPdfDocument); }
+
+BitmapChecksum PDFiumPage::getChecksum()
+{
+    size_t nPageWidth = FPDF_GetPageWidth(mpPage);
+    size_t nPageHeight = FPDF_GetPageHeight(mpPage);
+    FPDF_BITMAP pPdfBitmap = FPDFBitmap_Create(nPageWidth, nPageHeight, /*alpha=*/1);
+    if (!pPdfBitmap)
+    {
+        return 0;
+    }
+
+    // Intentionally not using FPDF_ANNOT here, annotations/commenting is OK to not affect the
+    // checksum, signature verification wants this.
+    FPDF_RenderPageBitmap(pPdfBitmap, mpPage, /*start_x=*/0, /*start_y=*/0, nPageWidth, nPageHeight,
+                          /*rotate=*/0, /*flags=*/0);
+    Bitmap aBitmap(Size(nPageWidth, nPageHeight), 24);
+    {
+        BitmapScopedWriteAccess pWriteAccess(aBitmap);
+        const auto pPdfBuffer = static_cast<ConstScanline>(FPDFBitmap_GetBuffer(pPdfBitmap));
+        const int nStride = FPDFBitmap_GetStride(pPdfBitmap);
+        for (size_t nRow = 0; nRow < nPageHeight; ++nRow)
+        {
+            ConstScanline pPdfLine = pPdfBuffer + (nStride * nRow);
+            pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
+        }
+    }
+    return aBitmap.GetChecksum();
+}
 }
 } // end vcl::pdf
 
diff --git a/xmlsecurity/Library_xmlsecurity.mk b/xmlsecurity/Library_xmlsecurity.mk
index 9a65dd2152a9..0ad912d5e0cc 100644
--- a/xmlsecurity/Library_xmlsecurity.mk
+++ b/xmlsecurity/Library_xmlsecurity.mk
@@ -20,7 +20,10 @@ $(eval $(call gb_Library_add_defs,xmlsecurity,\
     -DXMLSECURITY_DLLIMPLEMENTATION \
 ))
 
-$(eval $(call gb_Library_use_externals,xmlsecurity,boost_headers))
+$(eval $(call gb_Library_use_externals,xmlsecurity,\
+	boost_headers \
+	$(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \
+))
 
 $(eval $(call gb_Library_set_precompiled_header,xmlsecurity,$(SRCDIR)/xmlsecurity/inc/pch/precompiled_xmlsecurity))
 
diff --git a/xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf b/xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf
new file mode 100644
index 000000000000..f2b1a71096b2
Binary files /dev/null and b/xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf differ
diff --git a/xmlsecurity/qa/unit/signing/signing.cxx b/xmlsecurity/qa/unit/signing/signing.cxx
index bed9d0df8b0f..f9cdead4ae7f 100644
--- a/xmlsecurity/qa/unit/signing/signing.cxx
+++ b/xmlsecurity/qa/unit/signing/signing.cxx
@@ -96,6 +96,8 @@ public:
     void testPDFGood();
     /// Test a typical PDF where the signature is bad.
     void testPDFBad();
+    /// Test a maliciously manipulated signed pdf
+    void testPDFHideAndReplace();
     /// Test a typical PDF which is not signed.
     void testPDFNo();
 #endif
@@ -141,6 +143,7 @@ public:
 #if HAVE_FEATURE_PDFIMPORT
     CPPUNIT_TEST(testPDFGood);
     CPPUNIT_TEST(testPDFBad);
+    CPPUNIT_TEST(testPDFHideAndReplace);
     CPPUNIT_TEST(testPDFNo);
 #endif
     CPPUNIT_TEST(test96097Calc);
@@ -691,6 +694,22 @@ void SigningTest::testPDFBad()
                          static_cast<int>(pObjectShell->GetDocumentSignatureState()));
 }
 
+void SigningTest::testPDFHideAndReplace()
+{
+    createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY)
+              + "hide-and-replace-shadow-file-signed-2.pdf");
+    SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get());
+    CPPUNIT_ASSERT(pBaseModel);
+    SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+    CPPUNIT_ASSERT(pObjectShell);
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 2 (BROKEN)
+    // - Actual  : 6 (NOTVALIDATED_PARTIAL_OK)
+    // i.e. a non-commenting update after a signature was not marked as invalid.
+    CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN),
+                         static_cast<int>(pObjectShell->GetDocumentSignatureState()));
+}
+
 void SigningTest::testPDFNo()
 {
     createDoc(m_directories.getURLFromSrc(DATA_DIRECTORY) + "no.pdf");
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 7cf2c137c1c4..557180071a2c 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -12,6 +12,9 @@
 #include <memory>
 #include <vector>
 
+#include <config_features.h>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
 #include <rtl/string.hxx>
 #include <rtl/ustrbuf.hxx>
 #include <sal/log.hxx>
@@ -20,6 +23,7 @@
 #include <svl/sigstruct.hxx>
 #include <svl/cryptosign.hxx>
 #include <vcl/filter/pdfdocument.hxx>
+#include <vcl/bitmap.hxx>
 
 using namespace com::sun::star;
 
@@ -133,6 +137,66 @@ bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& rDocument,
     size_t nFileEnd = rStream.Tell();
     return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
 }
+
+/// Collects the checksum of each page of one version of the PDF.
+void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum>& rPageChecksums)
+{
+#if HAVE_FEATURE_PDFIUM
+    auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+    vcl::pdf::PDFiumDocument aPdfDocument(
+        FPDF_LoadMemDocument(rStream.GetData(), rStream.GetSize(), /*password=*/nullptr));
+
+    int nPageCount = aPdfDocument.getPageCount();
+    for (int nPage = 0; nPage < nPageCount; ++nPage)
+    {
+        std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage(aPdfDocument.openPage(nPage));
+        if (!pPdfPage)
+        {
+            return;
+        }
+
+        BitmapChecksum nPageChecksum = pPdfPage->getChecksum();
+        rPageChecksums.push_back(nPageChecksum);
+    }
+#else
+    (void)rStream;
+#endif
+}
+
+/**
+ * Checks if incremental updates after singing performed valid modifications only.
+ * Annotations/commenting is OK, other changes are not.
+ */
+bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature)
+{
+    size_t nSignatureEOF = 0;
+    if (!GetEOFOfSignature(pSignature, nSignatureEOF))
+    {
+        return false;
+    }
+
+    SvMemoryStream aSignatureStream;
+    sal_uInt64 nPos = rStream.Tell();
+    rStream.Seek(0);
+    aSignatureStream.WriteStream(rStream, nSignatureEOF);
+    rStream.Seek(nPos);
+    aSignatureStream.Seek(0);
+    std::vector<BitmapChecksum> aSignedPages;
+    AnalyizeSignatureStream(aSignatureStream, aSignedPages);
+
+    SvMemoryStream aFullStream;
+    nPos = rStream.Tell();
+    rStream.Seek(0);
+    aFullStream.WriteStream(rStream);
+    rStream.Seek(nPos);
+    aFullStream.Seek(0);
+    std::vector<BitmapChecksum> aAllPages;
+    AnalyizeSignatureStream(aFullStream, aAllPages);
+
+    // Fail if any page looks different after signing and at the end. Annotations/commenting doesn't
+    // count, though.
+    return aSignedPages == aAllPages;
+}
 }
 
 namespace xmlsecurity
@@ -247,6 +311,11 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat
         return false;
     }
     rInformation.bPartialDocumentSignature = !IsCompleteSignature(rStream, rDocument, pSignature);
+    if (!IsValidSignature(rStream, pSignature))
+    {
+        SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: invalid incremental update detected");
+        return false;
+    }
 
     // At this point there is no obviously missing info to validate the
     // signature.
diff --git a/xmlsecurity/workben/pdfverify.cxx b/xmlsecurity/workben/pdfverify.cxx
index 2754ea2f58c1..bc2978bb7c84 100644
--- a/xmlsecurity/workben/pdfverify.cxx
+++ b/xmlsecurity/workben/pdfverify.cxx
@@ -22,6 +22,7 @@
 #include <vcl/svapp.hxx>
 #include <vcl/graphicfilter.hxx>
 #include <vcl/filter/pdfdocument.hxx>
+#include <comphelper/scopeguard.hxx>
 
 #include <pdfio/pdfdocument.hxx>
 
@@ -79,11 +80,11 @@ int pdfVerify(int nArgc, char** pArgv)
                                                                     uno::UNO_QUERY);
     comphelper::setProcessServiceFactory(xMultiServiceFactory);
 
+    InitVCL();
+    comphelper::ScopeGuard g([] { DeInitVCL(); });
     if (nArgc > 3 && OString(pArgv[3]) == "-p")
     {
-        InitVCL();
         generatePreview(pArgv[1], pArgv[2]);
-        DeInitVCL();
         return 0;
     }
 
commit bdc1db1343d35d45182a1f6d983a3a823c16370a
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Jul 24 11:29:27 2020 +0200
Commit:     Vasily Melenchuk <vasily.melenchuk at cib.de>
CommitDate: Tue Apr 6 14:50:30 2021 +0300

    xmlsecurity: detect unsigned incremental update between signatures
    
    (cherry picked from commit 7468d5df5ec79783eae84b62bdc5ecf12f0ca255)
    
    Conflicts:
            vcl/source/filter/ipdf/pdfdocument.cxx
            xmlsecurity/source/pdfio/pdfdocument.cxx
    
    Change-Id: I269ed858852ee7d1275adf340c8cc1565fc30693
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/99480
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>

diff --git a/include/vcl/filter/pdfdocument.hxx b/include/vcl/filter/pdfdocument.hxx
index 3ec08020044a..56debd53e8c7 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -402,6 +402,8 @@ public:
     std::vector<PDFObjectElement*> GetSignatureWidgets();
     /// Remove the nth signature from read document in the edit buffer.
     bool RemoveSignature(size_t nPosition);
+    /// Get byte offsets of the end of incremental updates.
+    const std::vector<size_t>& GetEOFs() const;
     //@}
 };
 
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index 092df7cfbee7..84b65705b595 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -147,6 +147,8 @@ bool PDFDocument::RemoveSignature(size_t nPosition)
     return m_aEditBuffer.good();
 }
 
+const std::vector<size_t>& PDFDocument::GetEOFs() const { return m_aEOFs; }
+
 sal_uInt32 PDFDocument::GetNextSignature()
 {
     sal_uInt32 nRet = 0;
@@ -1978,7 +1980,16 @@ bool PDFCommentElement::Read(SvStream& rStream)
             m_aComment = aBuf.makeStringAndClear();
 
             if (m_aComment.startsWith("%%EOF"))
-                m_rDoc.PushBackEOF(rStream.Tell());
+            {
+                sal_uInt64 nPos = rStream.Tell();
+                if (ch == '\r')
+                {
+                    // If the comment ends with a \r\n, count the \n as well to match Adobe Acrobat
+                    // behavior.
+                    nPos += 1;
+                }
+                m_rDoc.PushBackEOF(nPos);
+            }
 
             SAL_INFO("vcl.filter", "PDFCommentElement::Read: m_aComment is '" << m_aComment << "'");
             return true;
diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index 996bb1527bb8..f7e36492e746 100644
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx
@@ -18,6 +18,7 @@ namespace vcl
 namespace filter
 {
 class PDFObjectElement;
+class PDFDocument;
 }
 }
 struct SignatureInformation;
@@ -29,12 +30,13 @@ namespace pdfio
 {
 /**
  * @param rInformation The actual result.
- * @param bLast If this is the last signature in the file, so it covers the whole file physically.
+ * @param rDocument the parsed document to see if the signature is partial.
  * @return If we can determinate a result.
  */
 XMLSECURITY_DLLPUBLIC bool ValidateSignature(SvStream& rStream,
                                              vcl::filter::PDFObjectElement* pSignature,
-                                             SignatureInformation& rInformation, bool bLast);
+                                             SignatureInformation& rInformation,
+                                             vcl::filter::PDFDocument& rDocument);
 
 } // namespace pdfio
 } // namespace xmlsecurity
diff --git a/xmlsecurity/qa/unit/pdfsigning/data/partial-in-between.pdf b/xmlsecurity/qa/unit/pdfsigning/data/partial-in-between.pdf
new file mode 100644
index 000000000000..211a111cb394
Binary files /dev/null and b/xmlsecurity/qa/unit/pdfsigning/data/partial-in-between.pdf differ
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index d608129d6254..8511f20eeae4 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -76,6 +76,7 @@ public:
     void testPDFPAdESGood();
     /// Test a valid signature that does not cover the whole file.
     void testPartial();
+    void testPartialInBetween();
     /// Test writing a PAdES signature.
     void testSigningCertificateAttribute();
     /// Test that we accept files which are supposed to be good.
@@ -97,6 +98,7 @@ public:
     CPPUNIT_TEST(testPDF14LOWin);
     CPPUNIT_TEST(testPDFPAdESGood);
     CPPUNIT_TEST(testPartial);
+    CPPUNIT_TEST(testPartialInBetween);
     CPPUNIT_TEST(testSigningCertificateAttribute);
     CPPUNIT_TEST(testGood);
     CPPUNIT_TEST(testTokenize);
@@ -143,9 +145,8 @@ std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, s
     for (size_t i = 0; i < aSignatures.size(); ++i)
     {
         SignatureInformation aInfo(i);
-        bool bLast = i == aSignatures.size() - 1;
         CPPUNIT_ASSERT(
-            xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[i], aInfo, bLast));
+            xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[i], aInfo, aVerifyDocument));
         aRet.push_back(aInfo);
 
         if (!rExpectedSubFilter.isEmpty())
@@ -287,7 +288,7 @@ void PDFSigningTest::testPDFRemove()
         CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aSignatures.size());
         SignatureInformation aInfo(0);
         CPPUNIT_ASSERT(
-            xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[0], aInfo, /*bLast=*/true));
+            xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[0], aInfo, aDocument));
     }
 
     // Remove the signature and write out the result as remove.pdf.
@@ -527,6 +528,18 @@ void PDFSigningTest::testUnknownSubFilter()
     CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(2), rInformations.size());
 }
 
+void PDFSigningTest::testPartialInBetween()
+{
+    std::vector<SignatureInformation> aInfos
+        = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "partial-in-between.pdf", 2,
+                 /*rExpectedSubFilter=*/OString());
+    CPPUNIT_ASSERT(!aInfos.empty());
+    SignatureInformation& rInformation = aInfos[0];
+    // Without the accompanying fix in place, this test would have failed, as unsigned incremental
+    // update between two signatures were not detected.
+    CPPUNIT_ASSERT(rInformation.bPartialDocumentSignature);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(PDFSigningTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
index 1f11b7856f4a..0398acac7ea0 100644
--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -57,8 +57,7 @@ bool PDFSignatureHelper::ReadAndVerifySignature(
     {
         SignatureInformation aInfo(i);
 
-        bool bLast = i == aSignatures.size() - 1;
-        if (!xmlsecurity::pdfio::ValidateSignature(*pStream, aSignatures[i], aInfo, bLast))
+        if (!xmlsecurity::pdfio::ValidateSignature(*pStream, aSignatures[i], aInfo, aDocument))
             SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
 
         m_aSignatureInfos.push_back(aInfo);
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index febcd2e3a1ea..7cf2c137c1c4 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -23,12 +23,124 @@
 
 using namespace com::sun::star;
 
+namespace
+{
+/// Turns an array of floats into offset + length pairs.
+bool GetByteRangesFromPDF(vcl::filter::PDFArrayElement& rArray,
+                          std::vector<std::pair<size_t, size_t>>& rByteRanges)
+{
+    size_t nByteRangeOffset = 0;
+    const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = rArray.GetElements();
+    for (size_t i = 0; i < rByteRangeElements.size(); ++i)
+    {
+        auto pNumber = dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
+        if (!pNumber)
+        {
+            SAL_WARN("xmlsecurity.pdfio",
+                     "ValidateSignature: signature offset and length has to be a number");
+            return false;
+        }
+
+        if (i % 2 == 0)
+        {
+            nByteRangeOffset = pNumber->GetValue();
+            continue;
+        }
+        size_t nByteRangeLength = pNumber->GetValue();
+        rByteRanges.emplace_back(nByteRangeOffset, nByteRangeLength);
+    }
+
+    return true;
+}
+
+/// Determines the last position that is covered by a signature.
+bool GetEOFOfSignature(vcl::filter::PDFObjectElement* pSignature, size_t& rEOF)
+{
+    vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
+    if (!pValue)
+    {
+        return false;
+    }
+
+    auto pByteRange = dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
+    if (!pByteRange || pByteRange->GetElements().size() < 2)
+    {
+        return false;
+    }
+
+    std::vector<std::pair<size_t, size_t>> aByteRanges;
+    if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
+    {
+        return false;
+    }
+
+    rEOF = aByteRanges[1].first + aByteRanges[1].second;
+    return true;
+}
+
+/// Checks if there are unsigned incremental updates between the signatures or after the last one.
+bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& rDocument,
+                         vcl::filter::PDFObjectElement* pSignature)
+{
+    std::set<size_t> aSignedEOFs;
+    for (const auto& i : rDocument.GetSignatureWidgets())
+    {
+        size_t nEOF = 0;
+        if (!GetEOFOfSignature(i, nEOF))
+        {
+            return false;
+        }
+
+        aSignedEOFs.insert(nEOF);
+    }
+
+    size_t nSignatureEOF = 0;
+    if (!GetEOFOfSignature(pSignature, nSignatureEOF))
+    {
+        return false;
+    }
+
+    const std::vector<size_t>& rAllEOFs = rDocument.GetEOFs();
+    bool bFoundOwn = false;
+    for (const auto& rEOF : rAllEOFs)
+    {
+        if (rEOF == nSignatureEOF)
+        {
+            bFoundOwn = true;
+            continue;
+        }
+
+        if (!bFoundOwn)
+        {
+            continue;
+        }
+
+        if (aSignedEOFs.find(rEOF) == aSignedEOFs.end())
+        {
+            // Unsigned incremental update found.
+            return false;
+        }
+    }
+
+    // Make sure we find the incremental update of the signature itself.
+    if (!bFoundOwn)
+    {
+        return false;
+    }
+
+    // No additional content after the last incremental update.
+    rStream.Seek(STREAM_SEEK_TO_END);
+    size_t nFileEnd = rStream.Tell();
+    return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
+}
+}
+
 namespace xmlsecurity
 {
 namespace pdfio
 {
 bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature,
-                       SignatureInformation& rInformation, bool bLast)
+                       SignatureInformation& rInformation, vcl::filter::PDFDocument& rDocument)
 {
     vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
     if (!pValue)
@@ -110,25 +222,9 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat
 
     // Build a list of offset-length pairs, representing the signed bytes.
     std::vector<std::pair<size_t, size_t>> aByteRanges;
-    size_t nByteRangeOffset = 0;
-    const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = pByteRange->GetElements();
-    for (size_t i = 0; i < rByteRangeElements.size(); ++i)
+    if (!GetByteRangesFromPDF(*pByteRange, aByteRanges))
     {
-        auto pNumber = dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
-        if (!pNumber)
-        {
-            SAL_WARN("xmlsecurity.pdfio",
-                     "ValidateSignature: signature offset and length has to be a number");
-            return false;
-        }
-
-        if (i % 2 == 0)
-        {
-            nByteRangeOffset = pNumber->GetValue();
-            continue;
-        }
-        size_t nByteRangeLength = pNumber->GetValue();
-        aByteRanges.emplace_back(nByteRangeOffset, nByteRangeLength);
+        return false;
     }
 
     // Detect if the byte ranges don't cover everything, but the signature itself.
@@ -150,11 +246,7 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat
                  "ValidateSignature: second range start is not the end of the signature");
         return false;
     }
-    rStream.Seek(STREAM_SEEK_TO_END);
-    size_t nFileEnd = rStream.Tell();
-    if (bLast && (aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
-        // Second range end is not the end of the file.
-        rInformation.bPartialDocumentSignature = true;
+    rInformation.bPartialDocumentSignature = !IsCompleteSignature(rStream, rDocument, pSignature);
 
     // At this point there is no obviously missing info to validate the
     // signature.
diff --git a/xmlsecurity/workben/pdfverify.cxx b/xmlsecurity/workben/pdfverify.cxx
index efe0461b9f14..2754ea2f58c1 100644
--- a/xmlsecurity/workben/pdfverify.cxx
+++ b/xmlsecurity/workben/pdfverify.cxx
@@ -158,8 +158,8 @@ int pdfVerify(int nArgc, char** pArgv)
             for (size_t i = 0; i < aSignatures.size(); ++i)
             {
                 SignatureInformation aInfo(i);
-                bool bLast = i == aSignatures.size() - 1;
-                if (!xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[i], aInfo, bLast))
+                if (!xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[i], aInfo,
+                                                           aDocument))
                 {
                     SAL_WARN("xmlsecurity.pdfio", "failed to determine digest match");
                     return 1;
@@ -168,6 +168,8 @@ int pdfVerify(int nArgc, char** pArgv)
                 bool bSuccess
                     = aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
                 std::cerr << "signature #" << i << ": digest match? " << bSuccess << std::endl;
+                std::cerr << "signature #" << i << ": partial? " << aInfo.bPartialDocumentSignature
+                          << std::endl;
             }
         }
 
commit cbecfdcdfbadf189d318c828ce66c46af71e1ded
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sun May 31 11:50:20 2020 +0200
Commit:     Vasily Melenchuk <vasily.melenchuk at cib.de>
CommitDate: Tue Apr 6 14:43:34 2021 +0300

    pdfium: only init pdfium library once and destroy on LO exit
    
    With more and more usage of PDFium, it is hard to keep track of
    the life-time of the PDFium library, so it can happen that a
    FPDF_DestroyLibrary happens when we still have another instance
    where PDFium is still use. The result of this is a crash. To
    prevent this, just initialize the library once and delete, when
    on LO exit.
    
    This can be improved in the future to only keep the library
    active when in actual use.
    
    [ Leaving out the vector graphic search bits, the motivation is to just
    have this in libreoffice-7-0, so that recent pdf sig verify improvements
    can be backported. ]
    
    (cherry picked from commit 067a8a954c8e1d8d6465a4ab5fb61e93f16c26c2)
    
    Conflicts:
            vcl/source/graphic/VectorGraphicSearch.cxx
            svx/source/svdraw/svdpdf.cxx
            svx/source/svdraw/svdpdf.hxx
            vcl/Library_vcl.mk
            vcl/qa/cppunit/pdfexport/pdfexport.cxx
            vcl/source/filter/ipdf/pdfread.cxx
    
    Change-Id: I5c7e5de7f8b97d10efb394c67c7a61b976c8d57c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102317
    Tested-by: Miklos Vajna <vmiklos at collabora.com>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
    (cherry picked from commit b7de766b4dc5b4810277069bcf53a9f3737e87da)

diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 343ae06e67da..aa013abc0901 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -205,9 +205,6 @@ void PdfExportTest::setUp()
 
 void PdfExportTest::tearDown()
 {
-    FPDF_ClosePage(mpPdfPage);
-    FPDF_CloseDocument(mpPdfDocument);
-
     if (mxComponent.is())
         mxComponent->dispose();
 
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
index fdefe60f9ec5..958df291f964 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -23,6 +23,8 @@
 
 #include <vcl/filter/PDFiumLibrary.hxx>
 
+#include <vcl/filter/PDFiumLibrary.hxx>
+
 using namespace com::sun::star;
 
 namespace
commit 4d54747a206ec7a1a7e7be38be456f89423db2a1
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Wed Sep 2 12:37:18 2020 +0200
Commit:     Vasily Melenchuk <vasily.melenchuk at cib.de>
CommitDate: Tue Apr 6 14:24:23 2021 +0300

    xmlsecurity: avoid saying OK when the signature is partial
    
    That's reserved for valid signatures (digest match, certificate
    validates and the signature covers the whole document).
    
    Also avoid "invalid" in the dialog when the digest matches and the
    signature is just incomplete.
    
    This now uses wording which is closer to Acrobat and also uses the same
    wording on the infobar and in the dialog.
    
    Conflicts:
            xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
    
    Change-Id: I26e4781d555b65cf29aa8df2232e286917235dc1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101926
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
    Tested-by: Jenkins
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102188
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    (cherry picked from commit 46efad443472679b93b282c8e08b807d7e8f1a78)

diff --git a/include/sfx2/strings.hrc b/include/sfx2/strings.hrc
index a77dbc86d77d..3e7b9599aea0 100644
--- a/include/sfx2/strings.hrc
+++ b/include/sfx2/strings.hrc
@@ -261,8 +261,8 @@
 #define STR_READONLY_SIGN                       NC_("STR_READONLY_SIGN", "Sign Document")
 #define STR_SIGNATURE_BROKEN                    NC_("STR_SIGNATURE_BROKEN", "This document has an invalid signature.")
 #define STR_SIGNATURE_INVALID                   NC_("STR_SIGNATURE_INVALID", "The signature was valid, but the document has been modified")
-#define STR_SIGNATURE_NOTVALIDATED              NC_("STR_SIGNATURE_NOTVALIDATED", "The signature is OK, but the certificate could not be validated.")
-#define STR_SIGNATURE_PARTIAL_OK                NC_("STR_SIGNATURE_PARTIAL_OK", "The signature is OK, but the document is only partially signed.")
+#define STR_SIGNATURE_NOTVALIDATED              NC_("STR_SIGNATURE_NOTVALIDATED", "At least one signature has problems: the certificate could not be validated.")
+#define STR_SIGNATURE_PARTIAL_OK                NC_("STR_SIGNATURE_PARTIAL_OK", "At least one signature has problems: the document is only partially signed.")
 #define STR_SIGNATURE_NOTVALIDATED_PARTIAL_OK   NC_("STR_SIGNATURE_NOTVALIDATED_PARTIAL_OK", "The certificate could not be validated and the document is only partially signed.")
 #define STR_SIGNATURE_OK                        NC_("STR_SIGNATURE_OK", "This document is digitally signed and the signature is valid.")
 #define STR_SIGNATURE_SHOW                      NC_("STR_SIGNATURE_SHOW", "Show Signatures")
diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
index 49cf10034603..a965401fcceb 100644
--- a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
+++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
@@ -558,6 +558,7 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox()
     size_t nInfos = maSignatureManager.maCurrentSignatureInformations.size();
     size_t nValidSigs = 0, nValidCerts = 0;
     bool bAllNewSignatures = true;
+    bool bSomePartial = false;
 
     if( nInfos )
     {
@@ -634,7 +635,7 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox()
             {
                 if (maSignatureManager.mxStore.is())
                 {
-                    // XML based.
+                    // ZIP based.
                     bSigValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
                           aElementsToBeVerified, rInfo, mode);
                 }
@@ -646,6 +647,10 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox()
 
                 if( bSigValid )
                     nValidSigs++;
+                else
+                {
+                    bSomePartial = true;
+                }
             }
 
             Image aImage;
@@ -698,8 +703,8 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox()
 
     bool bShowInvalidState = nInfos && !bAllSigsValid;
 
-    m_pSigsInvalidImg->Show( bShowInvalidState );
-    m_pSigsInvalidFI->Show( bShowInvalidState );
+    m_pSigsInvalidImg->Show( bShowInvalidState && !bSomePartial);
+    m_pSigsInvalidFI->Show( bShowInvalidState && !bSomePartial);
 
     bool bShowNotValidatedState = nInfos && bAllSigsValid && !bAllCertsValid;
 
@@ -708,8 +713,8 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox()
 
     //bAllNewSignatures is always true if we are not in document mode
     bool bShowOldSignature = nInfos && bAllSigsValid && bAllCertsValid && !bAllNewSignatures;
-    m_pSigsOldSignatureImg->Show(bShowOldSignature);
-    m_pSigsOldSignatureFI->Show(bShowOldSignature);
+    m_pSigsOldSignatureImg->Show(bShowOldSignature || bSomePartial);
+    m_pSigsOldSignatureFI->Show(bShowOldSignature || bSomePartial);
 
     SignatureHighlightHdl( nullptr );
 }
diff --git a/xmlsecurity/uiconfig/ui/digitalsignaturesdialog.ui b/xmlsecurity/uiconfig/ui/digitalsignaturesdialog.ui
index 958b8359bc80..55ee1f4e6c9b 100644
--- a/xmlsecurity/uiconfig/ui/digitalsignaturesdialog.ui
+++ b/xmlsecurity/uiconfig/ui/digitalsignaturesdialog.ui
@@ -297,7 +297,7 @@
                     <property name="can_focus">False</property>
                     <property name="no_show_all">True</property>
                     <property name="hexpand">True</property>
-                    <property name="label" translatable="yes" context="digitalsignaturesdialog|oldsignatureft">Not all parts of the document are signed</property>
+                    <property name="label" translatable="yes" context="digitalsignaturesdialog|oldsignatureft">At least one signature has problems: the document is only partially signed.</property>
                     <property name="xalign">0</property>
                   </object>
                   <packing>
@@ -310,7 +310,7 @@
                     <property name="can_focus">False</property>
                     <property name="no_show_all">True</property>
                     <property name="hexpand">True</property>
-                    <property name="label" translatable="yes" context="digitalsignaturesdialog|notvalidatedft">Certificate could not be validated</property>
+                    <property name="label" translatable="yes" context="digitalsignaturesdialog|notvalidatedft">At least one signature has problems: the certificate could not be validated.</property>
                     <property name="xalign">0</property>
                   </object>
                   <packing>
commit 863496186f9548571d9ed7597db9be0404ee63f6
Author:     Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Mon Aug 31 13:34:17 2020 +0200
Commit:     Vasily Melenchuk <vasily.melenchuk at cib.de>
CommitDate: Tue Apr 6 14:23:38 2021 +0300

    xmlsecurity: fix infobar vs signature dialog inconsistency
    
    The infobar mentioned if a signature is partial, but the dialog just has
    a bool UI for signatures. Then present "good, but partial" as "bad".
    
    (cherry picked from commit 3ba1144cb96c710e665ffb3ada26fb6a48a03472)
    
    Change-Id: I698190aa77702000b11d635bd038d9c9a91614ac
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/101844
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
index 584266f9c7a8..49cf10034603 100644
--- a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
+++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
@@ -632,8 +632,17 @@ void DigitalSignaturesDialog::ImplFillSignaturesBox()
 
             if ( bSigValid )
             {
-                 bSigValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
-                      aElementsToBeVerified, rInfo, mode);
+                if (maSignatureManager.mxStore.is())
+                {
+                    // XML based.
+                    bSigValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
+                          aElementsToBeVerified, rInfo, mode);
+                }
+                else
+                {
+                    // Assume PDF.
+                    bSigValid = !rInfo.bPartialDocumentSignature;
+                }
 
                 if( bSigValid )
                     nValidSigs++;
commit 727c3c5e799626c429548bbed868003f6200cc7c
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sun May 31 11:50:20 2020 +0200
Commit:     Vasily Melenchuk <vasily.melenchuk at cib.de>
CommitDate: Tue Apr 6 13:32:24 2021 +0300

    pdfium: only init pdfium library once and destroy on LO exit
    
    With more and more usage of PDFium, it is hard to keep track of
    the life-time of the PDFium library, so it can happen that a
    FPDF_DestroyLibrary happens when we still have another instance
    where PDFium is still use. The result of this is a crash. To
    prevent this, just initialize the library once and delete, when
    on LO exit.
    
    This can be improved in the future to only keep the library
    active when in actual use.
    
    [ Leaving out the vector graphic search bits, the motivation is to just
    have this in libreoffice-7-0, so that recent pdf sig verify improvements
    can be backported. ]
    
    (cherry picked from commit 067a8a954c8e1d8d6465a4ab5fb61e93f16c26c2)
    
    Conflicts:
            vcl/source/graphic/VectorGraphicSearch.cxx
    
    Change-Id: I5c7e5de7f8b97d10efb394c67c7a61b976c8d57c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102317
    Tested-by: Miklos Vajna <vmiklos at collabora.com>
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
    (cherry picked from commit b7de766b4dc5b4810277069bcf53a9f3737e87da)
    
    Conflicts:
            svx/source/svdraw/svdpdf.cxx
            svx/source/svdraw/svdpdf.hxx
            vcl/Library_vcl.mk
            vcl/qa/cppunit/pdfexport/pdfexport.cxx
            vcl/source/filter/ipdf/pdfread.cxx

diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx
new file mode 100644
index 000000000000..b9bceabb8acf
--- /dev/null
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <config_features.h>
+
+#if HAVE_FEATURE_PDFIUM
+
+#include <memory>
+#include <rtl/instance.hxx>
+#include <vcl/dllapi.h>
+
+namespace vcl
+{
+namespace pdf
+{
+class VCL_DLLPUBLIC PDFium final
+{
+private:
+    PDFium(const PDFium&) = delete;
+    PDFium& operator=(const PDFium&) = delete;
+
+public:
+    PDFium();
+    ~PDFium();
+};
+
+struct PDFiumLibrary : public rtl::StaticWithInit<std::shared_ptr<PDFium>, PDFiumLibrary>
+{
+    std::shared_ptr<PDFium> operator()() { return std::make_shared<PDFium>(); }
+};
+}
+} // namespace vcl::pdf
+
+#endif // HAVE_FEATURE_PDFIUM
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index b68252642bec..be7712686a4b 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -302,6 +302,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
     vcl/source/gdi/wall \
     vcl/source/gdi/scrptrun \
     vcl/source/gdi/CommonSalLayout \
+    vcl/source/pdf/PDFiumLibrary \
     vcl/source/graphic/GraphicLoader \
     vcl/source/graphic/GraphicObject \
     vcl/source/graphic/GraphicObject2 \
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index bcc4d11d1682..343ae06e67da 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -32,6 +32,8 @@
 #include <fpdf_text.h>
 #include <fpdfview.h>
 
+#include <vcl/filter/PDFiumLibrary.hxx>
+
 using namespace ::com::sun::star;
 
 static std::ostream& operator<<(std::ostream& rStrm, const Color& rColor)
@@ -77,6 +79,7 @@ class PdfExportTest : public test::BootstrapFixture, public unotest::MacrosTest
     SvMemoryStream maMemory;
     // Export the document as PDF, then parse it with PDFium.
     DocumentHolder exportAndParse(const OUString& rURL, const utl::MediaDescriptor& rDescriptor);
+    std::shared_ptr<vcl::pdf::PDFium> mpPDFium;
 
 public:
     PdfExportTest();
@@ -197,17 +200,13 @@ void PdfExportTest::setUp()
     mxComponentContext.set(comphelper::getComponentContext(getMultiServiceFactory()));
     mxDesktop.set(frame::Desktop::create(mxComponentContext));
 
-    FPDF_LIBRARY_CONFIG config;
-    config.version = 2;
-    config.m_pUserFontPaths = nullptr;
-    config.m_pIsolate = nullptr;
-    config.m_v8EmbedderSlot = 0;
-    FPDF_InitLibraryWithConfig(&config);
+    mpPDFium = vcl::pdf::PDFiumLibrary::get();
 }
 
 void PdfExportTest::tearDown()
 {
-    FPDF_DestroyLibrary();
+    FPDF_ClosePage(mpPdfPage);
+    FPDF_CloseDocument(mpPdfDocument);
 
     if (mxComponent.is())
         mxComponent->dispose();
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
index 9815e89a1a2b..fdefe60f9ec5 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -21,6 +21,8 @@
 #include <bitmapwriteaccess.hxx>
 #include <unotools/ucbstreamhelper.hxx>
 
+#include <vcl/filter/PDFiumLibrary.hxx>
+
 using namespace com::sun::star;
 
 namespace
@@ -63,12 +65,7 @@ size_t generatePreview(SvStream& rStream, std::vector<Bitmap>& rBitmaps, sal_uIn
                        sal_uInt64 nSize, const size_t nFirstPage, int nPages,
                        const double fResolutionDPI = 96.)
 {
-    FPDF_LIBRARY_CONFIG aConfig;
-    aConfig.version = 2;
-    aConfig.m_pUserFontPaths = nullptr;
-    aConfig.m_pIsolate = nullptr;
-    aConfig.m_v8EmbedderSlot = 0;
-    FPDF_InitLibraryWithConfig(&aConfig);
+    auto pPdfium = vcl::pdf::PDFiumLibrary::get();
 
     // Read input into a buffer.
     SvMemoryStream aInBuffer;
@@ -124,7 +121,6 @@ size_t generatePreview(SvStream& rStream, std::vector<Bitmap>& rBitmaps, sal_uIn
     }
 
     FPDF_CloseDocument(pPdfDocument);
-    FPDF_DestroyLibrary();
 
     return rBitmaps.size();
 }
@@ -164,12 +160,7 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream, sal_uInt64 n
     else
     {
         // Downconvert to PDF-1.5.
-        FPDF_LIBRARY_CONFIG aConfig;
-        aConfig.version = 2;
-        aConfig.m_pUserFontPaths = nullptr;
-        aConfig.m_pIsolate = nullptr;
-        aConfig.m_v8EmbedderSlot = 0;
-        FPDF_InitLibraryWithConfig(&aConfig);
+        auto pPdfium = vcl::pdf::PDFiumLibrary::get();
 
         // Read input into a buffer.
         SvMemoryStream aInBuffer;
@@ -187,7 +178,6 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream, sal_uInt64 n
             return false;
 
         FPDF_CloseDocument(pPdfDocument);
-        FPDF_DestroyLibrary();
 
         aWriter.m_aStream.Seek(STREAM_SEEK_TO_BEGIN);
         rOutStream.WriteStream(aWriter.m_aStream);
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
new file mode 100644
index 000000000000..5082c2567cce
--- /dev/null
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <config_features.h>
+
+#if HAVE_FEATURE_PDFIUM
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <fpdf_doc.h>
+
+namespace vcl
+{
+namespace pdf
+{
+PDFium::PDFium()
+{
+    FPDF_LIBRARY_CONFIG aConfig;
+    aConfig.version = 2;
+    aConfig.m_pUserFontPaths = nullptr;
+    aConfig.m_pIsolate = nullptr;
+    aConfig.m_v8EmbedderSlot = 0;
+    FPDF_InitLibraryWithConfig(&aConfig);
+}
+
+PDFium::~PDFium() { FPDF_DestroyLibrary(); }
+}
+} // end vcl::pdf
+
+#endif // HAVE_FEATURE_PDFIUM
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list