[Libreoffice-commits] core.git: Branch 'distro/collabora/cp-6.0-29' - 11 commits - download.lst external/pdfium include/vcl RepositoryExternal.mk svx/source vcl/CppunitTest_vcl_filter_ipdf.mk vcl/Library_vcl.mk vcl/Module_vcl.mk vcl/qa vcl/source xmlsecurity/inc xmlsecurity/Library_xmlsecurity.mk xmlsecurity/qa xmlsecurity/source xmlsecurity/workben
Miklos Vajna (via logerrit)
logerrit at kemper.freedesktop.org
Mon Dec 21 09:09:46 UTC 2020
RepositoryExternal.mk | 1
download.lst | 4
external/pdfium/Library_pdfium.mk | 175
external/pdfium/UnpackedTarball_pdfium.mk | 22
external/pdfium/build.patch.1 | 120
external/pdfium/c++20-comparison.patch | 13
external/pdfium/configs/build_config.h | 6
external/pdfium/edit.patch.1 | 757 --
external/pdfium/icu.patch.1 | 13
external/pdfium/msvc2015.patch.1 | 202
external/pdfium/pdfium4137-numerics.patch.3 | 3364 ++++++++++
external/pdfium/ubsan.patch | 26
external/pdfium/visibility.patch.1 | 30
external/pdfium/windows7.patch.1 | 34
include/vcl/filter/PDFiumLibrary.hxx | 76
include/vcl/filter/pdfdocument.hxx | 8
svx/source/svdraw/svdpdf.cxx | 93
svx/source/svdraw/svdpdf.hxx | 4
vcl/CppunitTest_vcl_filter_ipdf.mk | 49
vcl/Library_vcl.mk | 1
vcl/Module_vcl.mk | 1
vcl/qa/cppunit/filter/ipdf/data/dict-array-dict.pdf | 55
vcl/qa/cppunit/filter/ipdf/ipdf.cxx | 112
vcl/qa/cppunit/pdfexport/pdfexport.cxx | 17
vcl/source/filter/ipdf/pdfdocument.cxx | 108
vcl/source/filter/ipdf/pdfread.cxx | 26
vcl/source/pdf/PDFiumLibrary.cxx | 74
xmlsecurity/Library_xmlsecurity.mk | 5
xmlsecurity/inc/pdfio/pdfdocument.hxx | 4
xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p1.pdf |binary
xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf |binary
xmlsecurity/qa/unit/pdfsigning/data/partial-in-between.pdf |binary
xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx | 58
xmlsecurity/qa/unit/signing/data/hide-and-replace-shadow-file-signed-2.pdf |binary
xmlsecurity/qa/unit/signing/signing.cxx | 18
xmlsecurity/source/helper/pdfsignaturehelper.cxx | 6
xmlsecurity/source/pdfio/pdfdocument.cxx | 263
xmlsecurity/workben/pdfverify.cxx | 12
38 files changed, 4660 insertions(+), 1097 deletions(-)
New commits:
commit 812dc150c7e3041f68405bac000f769606eeae25
Author: Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Wed Nov 4 21:39:04 2020 +0100
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Dec 21 10:07:07 2020 +0100
xmlsecurity: reject a few dangerous annotation types during pdf sig verify
(cherry picked from commit f231dacde9df1c4aa5f4e0970535c4f4093364a7)
Conflicts:
include/vcl/filter/PDFiumLibrary.hxx
xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
xmlsecurity/source/helper/pdfsignaturehelper.cxx
xmlsecurity/source/pdfio/pdfdocument.cxx
Change-Id: I950b49a6e7181639daf27348ddfa0f36586baa65
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107969
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
diff --git a/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf b/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf
new file mode 100644
index 000000000000..b30f5b03867c
Binary files /dev/null and b/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p3-stamp.pdf differ
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index 899a7567c4a3..91e565cf2813 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -73,6 +73,7 @@ public:
void testPartial();
void testPartialInBetween();
void testBadCertP1();
+ void testBadCertP3Stamp();
/// Test writing a PAdES signature.
void testSigningCertificateAttribute();
/// Test that we accept files which are supposed to be good.
@@ -96,6 +97,7 @@ public:
CPPUNIT_TEST(testPartial);
CPPUNIT_TEST(testPartialInBetween);
CPPUNIT_TEST(testBadCertP1);
+ CPPUNIT_TEST(testBadCertP3Stamp);
CPPUNIT_TEST(testSigningCertificateAttribute);
CPPUNIT_TEST(testGood);
CPPUNIT_TEST(testTokenize);
@@ -419,6 +421,22 @@ void PDFSigningTest::testBadCertP1()
rInformation.nStatus);
}
+void PDFSigningTest::testBadCertP3Stamp()
+{
+ std::vector<SignatureInformation> aInfos
+ = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad-cert-p3-stamp.pdf", 1,
+ /*rExpectedSubFilter=*/OString());
+ CPPUNIT_ASSERT(!aInfos.empty());
+ SignatureInformation& rInformation = aInfos[0];
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 0 (SecurityOperationStatus_UNKNOWN)
+ // - Actual : 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
+ // i.e. adding a stamp annotation was not considered as a bad modification.
+ CPPUNIT_ASSERT_EQUAL(xml::crypto::SecurityOperationStatus::SecurityOperationStatus_UNKNOWN,
+ rInformation.nStatus);
+}
+
void PDFSigningTest::testSigningCertificateAttribute()
{
// Create a new signature.
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 7b697d6d86eb..160bbee6bcde 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -38,6 +38,11 @@
#include <svl/sigstruct.hxx>
#include <svl/cryptosign.hxx>
#include <vcl/bitmap.hxx>
+#include <basegfx/range/b2drectangle.hxx>
+
+#if HAVE_FEATURE_PDFIUM
+#include <fpdf_annot.h>
+#endif
#ifdef XMLSEC_CRYPTO_NSS
#include <cert.h>
@@ -167,8 +172,29 @@ bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& rDocument,
return std::find(rAllEOFs.begin(), rAllEOFs.end(), nFileEnd) != rAllEOFs.end();
}
+/**
+ * Contains checksums of a PDF page, which is rendered without annotations. It also contains
+ * the geometry of a few dangerous annotation types.
+ */
+struct PageChecksum
+{
+ BitmapChecksum m_nPageContent;
+ std::vector<basegfx::B2DRectangle> m_aAnnotations;
+ bool operator==(const PageChecksum& rChecksum) const;
+};
+
+bool PageChecksum::operator==(const PageChecksum& rChecksum) const
+{
+ if (m_nPageContent != rChecksum.m_nPageContent)
+ {
+ return false;
+ }
+
+ return m_aAnnotations == rChecksum.m_aAnnotations;
+}
+
/// Collects the checksum of each page of one version of the PDF.
-void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum>& rPageChecksums,
+void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<PageChecksum>& rPageChecksums,
int nMDPPerm)
{
#if HAVE_FEATURE_PDFIUM
@@ -185,8 +211,35 @@ void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum
return;
}
- BitmapChecksum nPageChecksum = pPdfPage->getChecksum(nMDPPerm);
- rPageChecksums.push_back(nPageChecksum);
+ PageChecksum aPageChecksum;
+ aPageChecksum.m_nPageContent = pPdfPage->getChecksum(nMDPPerm);
+ for (int i = 0; i < FPDFPage_GetAnnotCount(pPdfPage->getPointer()); ++i)
+ {
+ FPDF_ANNOTATION pAnnotation = FPDFPage_GetAnnot(pPdfPage->getPointer(), i);
+ int nType = FPDFAnnot_GetSubtype(pAnnotation);
+ switch (nType)
+ {
+ case FPDF_ANNOT_UNKNOWN:
+ case FPDF_ANNOT_FREETEXT:
+ case FPDF_ANNOT_STAMP:
+ case FPDF_ANNOT_REDACT:
+ {
+ basegfx::B2DRectangle aB2DRectangle;
+ FS_RECTF aRect;
+ if (FPDFAnnot_GetRect(pAnnotation, &aRect))
+ {
+ aB2DRectangle = basegfx::B2DRectangle(aRect.left, aRect.top, aRect.right,
+ aRect.bottom);
+ }
+ aPageChecksum.m_aAnnotations.push_back(aB2DRectangle);
+ break;
+ }
+ default:
+ break;
+ }
+ FPDFPage_CloseAnnot(pAnnotation);
+ }
+ rPageChecksums.push_back(aPageChecksum);
}
FPDF_CloseDocument(pPdfDocument);
#else
@@ -212,7 +265,7 @@ bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignatu
aSignatureStream.WriteStream(rStream, nSignatureEOF);
rStream.Seek(nPos);
aSignatureStream.Seek(0);
- std::vector<BitmapChecksum> aSignedPages;
+ std::vector<PageChecksum> aSignedPages;
AnalyizeSignatureStream(aSignatureStream, aSignedPages, nMDPPerm);
SvMemoryStream aFullStream;
@@ -221,7 +274,7 @@ bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignatu
aFullStream.WriteStream(rStream);
rStream.Seek(nPos);
aFullStream.Seek(0);
- std::vector<BitmapChecksum> aAllPages;
+ std::vector<PageChecksum> aAllPages;
AnalyizeSignatureStream(aFullStream, aAllPages, nMDPPerm);
// Fail if any page looks different after signing and at the end. Annotations/commenting doesn't
commit 1d1c976734d87f600f2c947c38772db84e4a1041
Author: Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Mon Oct 19 16:50:07 2020 +0200
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Dec 21 10:07:07 2020 +0100
xmlsecurity: handle MDP permission during PDF verify
(cherry picked from commit 586f6abee92af3cdabdce034b607b9a046ed3946)
Conflicts:
include/vcl/filter/PDFiumLibrary.hxx
vcl/source/filter/ipdf/pdfdocument.cxx
vcl/source/pdf/PDFiumLibrary.cxx
xmlsecurity/inc/pdfio/pdfdocument.hxx
xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
xmlsecurity/source/helper/pdfsignaturehelper.cxx
(cherry picked from commit 00479937dc071246cc27f33fd6397668448a7ed9)
Change-Id: I626fca7c03079fb0374c577dcfe024e7db6ed5b3
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107966
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx
index 60ece6b7cbc9..34b28787eb37 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -60,7 +60,7 @@ public:
FPDF_PAGE getPointer() { return mpPage; }
/// Get bitmap checksum of the page, without annotations/commenting.
- BitmapChecksum getChecksum();
+ BitmapChecksum getChecksum(int nMDPPerm);
};
struct PDFiumLibrary : public rtl::StaticWithInit<std::shared_ptr<PDFium>, PDFiumLibrary>
diff --git a/include/vcl/filter/pdfdocument.hxx b/include/vcl/filter/pdfdocument.hxx
index 65f72f301c8f..a814a7da3c15 100644
--- a/include/vcl/filter/pdfdocument.hxx
+++ b/include/vcl/filter/pdfdocument.hxx
@@ -359,6 +359,7 @@ public:
size_t GetObjectOffset(size_t nIndex) const;
const std::vector< std::unique_ptr<PDFElement> >& GetElements();
std::vector<PDFObjectElement*> GetPages();
+ PDFObjectElement* GetCatalog();
/// Remember the end location of an EOF token.
void PushBackEOF(size_t nOffset);
/// Look up object based on object number, possibly by parsing object streams.
@@ -381,6 +382,11 @@ public:
bool Write(SvStream& rStream);
/// Get a list of signatures embedded into this document.
std::vector<PDFObjectElement*> GetSignatureWidgets();
+ /**
+ * Get the value of the "modification detection and prevention" permission:
+ * Valid values are 1, 2 and 3: only 3 allows annotations after signing.
+ */
+ int GetMDPPerm();
/// 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.
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index 6851bef19b91..b17ffb25999f 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -1793,10 +1793,8 @@ static void visitPages(PDFObjectElement* pPages, std::vector<PDFObjectElement*>&
pPages->setVisiting(false);
}
-std::vector<PDFObjectElement*> PDFDocument::GetPages()
+PDFObjectElement* PDFDocument::GetCatalog()
{
- std::vector<PDFObjectElement*> aRet;
-
PDFReferenceElement* pRoot = nullptr;
@@ -1817,11 +1815,18 @@ std::vector<PDFObjectElement*> PDFDocument::GetPages()
if (!pRoot)
{
- SAL_WARN("vcl.filter", "PDFDocument::GetPages: trailer has no Root key");
- return aRet;
+ SAL_WARN("vcl.filter", "PDFDocument::GetCatalog: trailer has no Root key");
+ return nullptr;
}
- PDFObjectElement* pCatalog = pRoot->LookupObject();
+ return pRoot->LookupObject();
+}
+
+std::vector<PDFObjectElement*> PDFDocument::GetPages()
+{
+ std::vector<PDFObjectElement*> aRet;
+
+ PDFObjectElement* pCatalog = GetCatalog();
if (!pCatalog)
{
SAL_WARN("vcl.filter", "PDFDocument::GetPages: trailer has no catalog");
@@ -1896,6 +1901,71 @@ std::vector<PDFObjectElement*> PDFDocument::GetSignatureWidgets()
return aRet;
}
+int PDFDocument::GetMDPPerm()
+{
+ int nRet = 3;
+
+ std::vector<PDFObjectElement*> aSignatures = GetSignatureWidgets();
+ if (aSignatures.empty())
+ {
+ return nRet;
+ }
+
+ for (const auto& pSignature : aSignatures)
+ {
+ vcl::filter::PDFObjectElement* pSig = pSignature->LookupObject("V");
+ if (!pSig)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::GetMDPPerm: can't find signature object");
+ continue;
+ }
+
+ auto pReference = dynamic_cast<PDFArrayElement*>(pSig->Lookup("Reference"));
+ if (!pReference || pReference->GetElements().empty())
+ {
+ continue;
+ }
+
+ auto pFirstReference = dynamic_cast<PDFDictionaryElement*>(pReference->GetElements()[0]);
+ if (!pFirstReference)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::GetMDPPerm: reference array doesn't contain a dictionary");
+ continue;
+ }
+
+ PDFElement* pTransformParams = pFirstReference->LookupElement("TransformParams");
+ auto pTransformParamsDict = dynamic_cast<PDFDictionaryElement*>(pTransformParams);
+ if (!pTransformParamsDict)
+ {
+ auto pTransformParamsRef = dynamic_cast<PDFReferenceElement*>(pTransformParams);
+ if (pTransformParamsRef)
+ {
+ PDFObjectElement* pTransformParamsObj = pTransformParamsRef->LookupObject();
+ if (pTransformParamsObj)
+ {
+ pTransformParamsDict = pTransformParamsObj->GetDictionary();
+ }
+ }
+ }
+
+ if (!pTransformParamsDict)
+ {
+ continue;
+ }
+
+ auto pP = dynamic_cast<PDFNumberElement*>(pTransformParamsDict->LookupElement("P"));
+ if (!pP)
+ {
+ return 2;
+ }
+
+ return pP->GetValue();
+ }
+
+ return nRet;
+}
+
std::vector<unsigned char> PDFDocument::DecodeHexString(PDFHexStringElement const* pElement)
{
return svl::crypto::DecodeHexString(pElement->GetValue());
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
index a8774b15bb4e..96dab5f62ff6 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -34,7 +34,7 @@ PDFium::PDFium()
PDFium::~PDFium() { FPDF_DestroyLibrary(); }
-BitmapChecksum PDFiumPage::getChecksum()
+BitmapChecksum PDFiumPage::getChecksum(int nMDPPerm)
{
size_t nPageWidth = FPDF_GetPageWidth(mpPage);
size_t nPageHeight = FPDF_GetPageHeight(mpPage);
@@ -44,10 +44,14 @@ BitmapChecksum PDFiumPage::getChecksum()
return 0;
}
- // Intentionally not using FPDF_ANNOT here, annotations/commenting is OK to not affect the
- // checksum, signature verification wants this.
+ int nFlags = 0;
+ if (nMDPPerm != 3)
+ {
+ // Annotations/commenting should affect the checksum, signature verification wants this.
+ nFlags = FPDF_ANNOT;
+ }
FPDF_RenderPageBitmap(pPdfBitmap, mpPage, /*start_x=*/0, /*start_y=*/0, nPageWidth, nPageHeight,
- /*rotate=*/0, /*flags=*/0);
+ /*rotate=*/0, nFlags);
Bitmap aBitmap(Size(nPageWidth, nPageHeight), 24);
{
Bitmap::ScopedWriteAccess pWriteAccess(aBitmap);
diff --git a/xmlsecurity/inc/pdfio/pdfdocument.hxx b/xmlsecurity/inc/pdfio/pdfdocument.hxx
index aaacae9db29f..5c8bf8b3557b 100644
--- a/xmlsecurity/inc/pdfio/pdfdocument.hxx
+++ b/xmlsecurity/inc/pdfio/pdfdocument.hxx
@@ -29,7 +29,7 @@ namespace pdfio
* @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, vcl::filter::PDFDocument& rDocument);
+XMLSECURITY_DLLPUBLIC bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature, SignatureInformation& rInformation, vcl::filter::PDFDocument& rDocument, int nMDPPerm);
} // namespace pdfio
} // namespace xmlsecurity
diff --git a/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p1.pdf b/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p1.pdf
new file mode 100644
index 000000000000..04d9950582b0
Binary files /dev/null and b/xmlsecurity/qa/unit/pdfsigning/data/bad-cert-p1.pdf differ
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index 54fe2e7912f5..899a7567c4a3 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -72,6 +72,7 @@ public:
/// Test a valid signature that does not cover the whole file.
void testPartial();
void testPartialInBetween();
+ void testBadCertP1();
/// Test writing a PAdES signature.
void testSigningCertificateAttribute();
/// Test that we accept files which are supposed to be good.
@@ -94,6 +95,7 @@ public:
CPPUNIT_TEST(testPDFPAdESGood);
CPPUNIT_TEST(testPartial);
CPPUNIT_TEST(testPartialInBetween);
+ CPPUNIT_TEST(testBadCertP1);
CPPUNIT_TEST(testSigningCertificateAttribute);
CPPUNIT_TEST(testGood);
CPPUNIT_TEST(testTokenize);
@@ -139,7 +141,9 @@ std::vector<SignatureInformation> PDFSigningTest::verify(const OUString& rURL, s
for (size_t i = 0; i < aSignatures.size(); ++i)
{
SignatureInformation aInfo(i);
- CPPUNIT_ASSERT(xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[i], aInfo, aVerifyDocument));
+ int nMDPPerm = aVerifyDocument.GetMDPPerm();
+ xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[i], aInfo, aVerifyDocument,
+ nMDPPerm);
aRet.push_back(aInfo);
if (!rExpectedSubFilter.isEmpty())
@@ -268,7 +272,9 @@ void PDFSigningTest::testPDFRemove()
std::vector<vcl::filter::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aSignatures.size());
SignatureInformation aInfo(0);
- CPPUNIT_ASSERT(xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[0], aInfo, aDocument));
+ int nMDPPerm = aDocument.GetMDPPerm();
+ CPPUNIT_ASSERT(xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[0], aInfo,
+ aDocument, nMDPPerm));
}
// Remove the signature and write out the result as remove.pdf.
@@ -398,6 +404,21 @@ void PDFSigningTest::testPartial()
CPPUNIT_ASSERT(rInformation.bPartialDocumentSignature);
}
+void PDFSigningTest::testBadCertP1()
+{
+ std::vector<SignatureInformation> aInfos
+ = verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "bad-cert-p1.pdf", 1,
+ /*rExpectedSubFilter=*/OString());
+ CPPUNIT_ASSERT(!aInfos.empty());
+ SignatureInformation& rInformation = aInfos[0];
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 0 (SecurityOperationStatus_UNKNOWN)
+ // - Actual : 1 (SecurityOperationStatus_OPERATION_SUCCEEDED)
+ // i.e. annotation after a P1 signature was not considered as a bad modification.
+ CPPUNIT_ASSERT_EQUAL(xml::crypto::SecurityOperationStatus::SecurityOperationStatus_UNKNOWN,
+ rInformation.nStatus);
+}
+
void PDFSigningTest::testSigningCertificateAttribute()
{
// Create a new signature.
diff --git a/xmlsecurity/source/helper/pdfsignaturehelper.cxx b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
index 5e7684ca24c3..1f809fda1148 100644
--- a/xmlsecurity/source/helper/pdfsignaturehelper.cxx
+++ b/xmlsecurity/source/helper/pdfsignaturehelper.cxx
@@ -48,11 +48,14 @@ bool PDFSignatureHelper::ReadAndVerifySignature(const uno::Reference<io::XInputS
m_aSignatureInfos.clear();
+ int nMDPPerm = aDocument.GetMDPPerm();
+
for (size_t i = 0; i < aSignatures.size(); ++i)
{
SignatureInformation aInfo(i);
- if (!xmlsecurity::pdfio::ValidateSignature(*pStream, aSignatures[i], aInfo, aDocument))
+ if (!xmlsecurity::pdfio::ValidateSignature(*pStream, aSignatures[i], aInfo, aDocument,
+ nMDPPerm))
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 21ea3c324d1a..7b697d6d86eb 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -168,7 +168,8 @@ bool IsCompleteSignature(SvStream& rStream, vcl::filter::PDFDocument& rDocument,
}
/// Collects the checksum of each page of one version of the PDF.
-void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum>& rPageChecksums)
+void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum>& rPageChecksums,
+ int nMDPPerm)
{
#if HAVE_FEATURE_PDFIUM
auto pPdfium = vcl::pdf::PDFiumLibrary::get();
@@ -184,7 +185,7 @@ void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum
return;
}
- BitmapChecksum nPageChecksum = pPdfPage->getChecksum();
+ BitmapChecksum nPageChecksum = pPdfPage->getChecksum(nMDPPerm);
rPageChecksums.push_back(nPageChecksum);
}
FPDF_CloseDocument(pPdfDocument);
@@ -195,9 +196,9 @@ void AnalyizeSignatureStream(SvMemoryStream& rStream, std::vector<BitmapChecksum
/**
* Checks if incremental updates after singing performed valid modifications only.
- * Annotations/commenting is OK, other changes are not.
+ * nMDPPerm decides if annotations/commenting is OK, other changes are always not.
*/
-bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature)
+bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature, int nMDPPerm)
{
size_t nSignatureEOF = 0;
if (!GetEOFOfSignature(pSignature, nSignatureEOF))
@@ -212,7 +213,7 @@ bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignatu
rStream.Seek(nPos);
aSignatureStream.Seek(0);
std::vector<BitmapChecksum> aSignedPages;
- AnalyizeSignatureStream(aSignatureStream, aSignedPages);
+ AnalyizeSignatureStream(aSignatureStream, aSignedPages, nMDPPerm);
SvMemoryStream aFullStream;
nPos = rStream.Tell();
@@ -221,7 +222,7 @@ bool IsValidSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignatu
rStream.Seek(nPos);
aFullStream.Seek(0);
std::vector<BitmapChecksum> aAllPages;
- AnalyizeSignatureStream(aFullStream, aAllPages);
+ AnalyizeSignatureStream(aFullStream, aAllPages, nMDPPerm);
// Fail if any page looks different after signing and at the end. Annotations/commenting doesn't
// count, though.
@@ -234,7 +235,7 @@ namespace xmlsecurity
namespace pdfio
{
-bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature, SignatureInformation& rInformation, vcl::filter::PDFDocument& rDocument)
+bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature, SignatureInformation& rInformation, vcl::filter::PDFDocument& rDocument, int nMDPPerm)
{
vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
if (!pValue)
@@ -337,7 +338,7 @@ bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignat
return false;
}
rInformation.bPartialDocumentSignature = !IsCompleteSignature(rStream, rDocument, pSignature);
- if (!IsValidSignature(rStream, pSignature))
+ if (!IsValidSignature(rStream, pSignature, nMDPPerm))
{
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: invalid incremental update detected");
return false;
diff --git a/xmlsecurity/workben/pdfverify.cxx b/xmlsecurity/workben/pdfverify.cxx
index 92429864080b..bb66edb29981 100644
--- a/xmlsecurity/workben/pdfverify.cxx
+++ b/xmlsecurity/workben/pdfverify.cxx
@@ -147,11 +147,12 @@ int pdfVerify(int nArgc, char** pArgv)
else
{
std::cerr << "found " << aSignatures.size() << " signatures" << std::endl;
+ int nMDPPerm = aDocument.GetMDPPerm();
for (size_t i = 0; i < aSignatures.size(); ++i)
{
SignatureInformation aInfo(i);
if (!xmlsecurity::pdfio::ValidateSignature(aStream, aSignatures[i], aInfo,
- aDocument))
+ aDocument, nMDPPerm))
{
SAL_WARN("xmlsecurity.pdfio", "failed to determine digest match");
return 1;
commit c7912ae08cb37bb5e7fb3afec2bee278c63cf5d7
Author: Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Sep 4 17:17:48 2020 +0200
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Dec 21 10:07:07 2020 +0100
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
xmlsecurity/source/pdfio/pdfdocument.cxx
xmlsecurity/workben/pdfverify.cxx
Change-Id: I4607c242b3c6f6b01517b02407e9e7a095e2e069
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107944
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx
index 1f6d97045088..60ece6b7cbc9 100644
--- a/include/vcl/filter/PDFiumLibrary.hxx
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -17,6 +17,9 @@
#include <memory>
#include <rtl/instance.hxx>
#include <vcl/dllapi.h>
+#include <vcl/checksum.hxx>
+
+#include <fpdfview.h>
namespace vcl
{
@@ -33,6 +36,33 @@ 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);
+ }
+
+ FPDF_PAGE getPointer() { return mpPage; }
+
+ /// Get bitmap checksum of the page, without annotations/commenting.
+ BitmapChecksum getChecksum();
+};
+
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 9d822c34642c..a8774b15bb4e 100644
--- a/vcl/source/pdf/PDFiumLibrary.cxx
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -15,6 +15,9 @@
#include <vcl/filter/PDFiumLibrary.hxx>
#include <fpdf_doc.h>
+#include <vcl/bitmap.hxx>
+#include <vcl/bitmapaccess.hxx>
+
namespace vcl
{
namespace pdf
@@ -31,6 +34,34 @@ PDFium::PDFium()
PDFium::~PDFium() { FPDF_DestroyLibrary(); }
+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);
+ {
+ Bitmap::ScopedWriteAccess pWriteAccess(aBitmap);
+ const auto pPdfBuffer = static_cast<const sal_uInt8*>(FPDFBitmap_GetBuffer(pPdfBitmap));
+ const int nStride = FPDFBitmap_GetStride(pPdfBitmap);
+ for (size_t nRow = 0; nRow < nPageHeight; ++nRow)
+ {
+ const sal_uInt8* pPdfLine = pPdfBuffer + (nStride * nRow);
+ pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
+ }
+ }
+ return aBitmap.GetChecksum();
+}
+
} // end pdf
} // end vcl
diff --git a/xmlsecurity/Library_xmlsecurity.mk b/xmlsecurity/Library_xmlsecurity.mk
index 22d27c717155..26d1549089c2 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 265f0e5acbb0..6ea6275aaee6 100644
--- a/xmlsecurity/qa/unit/signing/signing.cxx
+++ b/xmlsecurity/qa/unit/signing/signing.cxx
@@ -93,6 +93,7 @@ public:
void testPDFGood();
/// Test a typical PDF where the signature is bad.
void testPDFBad();
+ void testPDFHideAndReplace();
/// Test a typical PDF which is not signed.
void testPDFNo();
#endif
@@ -133,6 +134,7 @@ public:
#if HAVE_FEATURE_PDFIMPORT
CPPUNIT_TEST(testPDFGood);
CPPUNIT_TEST(testPDFBad);
+ CPPUNIT_TEST(testPDFHideAndReplace);
CPPUNIT_TEST(testPDFNo);
#endif
CPPUNIT_TEST(test96097Calc);
@@ -497,6 +499,22 @@ void SigningTest::testPDFBad()
CPPUNIT_ASSERT_EQUAL(static_cast<int>(SignatureState::BROKEN), 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 8d6aeafdd9f3..21ea3c324d1a 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -13,6 +13,9 @@
#include <memory>
#include <vector>
+#include <config_features.h>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <comphelper/processfactory.hxx>
@@ -34,6 +37,7 @@
#include <svl/sigstruct.hxx>
#include <svl/cryptosign.hxx>
+#include <vcl/bitmap.hxx>
#ifdef XMLSEC_CRYPTO_NSS
#include <cert.h>
@@ -162,6 +166,67 @@ 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();
+ FPDF_DOCUMENT pPdfDocument(
+ FPDF_LoadMemDocument(rStream.GetData(), rStream.GetSize(), /*password=*/nullptr));
+
+ int nPageCount = FPDF_GetPageCount(pPdfDocument);
+ for (int nPage = 0; nPage < nPageCount; ++nPage)
+ {
+ auto pPdfPage = std::make_unique<vcl::pdf::PDFiumPage>(FPDF_LoadPage(pPdfDocument, nPage));
+ if (!pPdfPage)
+ {
+ return;
+ }
+
+ BitmapChecksum nPageChecksum = pPdfPage->getChecksum();
+ rPageChecksums.push_back(nPageChecksum);
+ }
+ FPDF_CloseDocument(pPdfDocument);
+#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
@@ -272,6 +337,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 7ddb2623944c..92429864080b 100644
--- a/xmlsecurity/workben/pdfverify.cxx
+++ b/xmlsecurity/workben/pdfverify.cxx
@@ -20,6 +20,7 @@
#include <vcl/pngwrite.hxx>
#include <vcl/svapp.hxx>
#include <vcl/graphicfilter.hxx>
+#include <comphelper/scopeguard.hxx>
#include <pdfio/pdfdocument.hxx>
@@ -72,11 +73,11 @@ int pdfVerify(int nArgc, char** pArgv)
uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xMultiComponentFactory, 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 f11d01696bb18616482804f789307b24ddf36174
Author: Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Sun May 31 11:50:20 2020 +0200
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Dec 21 10:07:07 2020 +0100
pdfium: only init pdfium library one 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.
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95391
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
(cherry picked from commit 067a8a954c8e1d8d6465a4ab5fb61e93f16c26c2)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95933
Tested-by: Tomaž Vajngerl <quikee at gmail.com>
Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>
(cherry picked from commit 3538b83c8d83e66f63c745bd769d118117704026)
Conflicts:
vcl/qa/cppunit/pdfexport/pdfexport.cxx
vcl/source/filter/ipdf/pdfread.cxx
vcl/source/graphic/VectorGraphicSearch.cxx
Change-Id: I5c7e5de7f8b97d10efb394c67c7a61b976c8d57c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107943
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
diff --git a/include/vcl/filter/PDFiumLibrary.hxx b/include/vcl/filter/PDFiumLibrary.hxx
new file mode 100644
index 000000000000..1f6d97045088
--- /dev/null
+++ b/include/vcl/filter/PDFiumLibrary.hxx
@@ -0,0 +1,46 @@
+/* -*- 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 pdf
+} // namespace pdf
+
+#endif // HAVE_FEATURE_PDFIUM
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
index 9e49a3d1e90a..78437f247123 100644
--- a/svx/source/svdraw/svdpdf.cxx
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -147,6 +147,7 @@ ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, SdrLayerID nLay, const tools:
, mnPageCount(0)
, mdPageWidthPts(0)
, mdPageHeightPts(0)
+ , mpPDFium(vcl::pdf::PDFiumLibrary::get())
{
mpVD->EnableOutput(false);
mpVD->SetLineColor();
@@ -160,13 +161,6 @@ ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, SdrLayerID nLay, const tools:
svl::Items<EE_ITEMS_START, EE_ITEMS_END>{});
checkClip();
- FPDF_LIBRARY_CONFIG aConfig;
- aConfig.version = 2;
- aConfig.m_pUserFontPaths = nullptr;
- aConfig.m_pIsolate = nullptr;
- aConfig.m_v8EmbedderSlot = 0;
- FPDF_InitLibraryWithConfig(&aConfig);
-
// Load the buffer using pdfium.
mpPdfDocument = FPDF_LoadMemDocument(mpPdfData->data(), mpPdfData->size(),
/*password=*/nullptr);
@@ -199,11 +193,7 @@ ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, SdrLayerID nLay, const tools:
mnPageCount = FPDF_GetPageCount(mpPdfDocument);
}
-ImpSdrPdfImport::~ImpSdrPdfImport()
-{
- FPDF_CloseDocument(mpPdfDocument);
- FPDF_DestroyLibrary();
-}
+ImpSdrPdfImport::~ImpSdrPdfImport() { FPDF_CloseDocument(mpPdfDocument); }
void ImpSdrPdfImport::DoObjects(SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport,
int nPageIndex)
diff --git a/svx/source/svdraw/svdpdf.hxx b/svx/source/svdraw/svdpdf.hxx
index df8eac35bbe7..ab880b930649 100644
--- a/svx/source/svdraw/svdpdf.hxx
+++ b/svx/source/svdraw/svdpdf.hxx
@@ -37,6 +37,8 @@
#include <fpdfview.h>
+#include <vcl/filter/PDFiumLibrary.hxx>
+
// Forward Declarations
class SfxItemSet;
@@ -203,6 +205,8 @@ class ImpSdrPdfImport final
tools::Rectangle PointsToLogic(double left, double right, double top, double bottom) const;
Point PointsToLogic(double x, double y) const;
+ std::shared_ptr<vcl::pdf::PDFium> mpPDFium;
+
// check for clip and evtl. fill maClip
void checkClip();
bool isClip() const;
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 337dba4803df..64d56b45a9d0 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -304,6 +304,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/bitmap/bitmapfilter \
vcl/source/bitmap/bitmapscalesuper \
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 0ac88bc96db9..d2a46756b89b 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -28,6 +28,8 @@
#include <fpdf_text.h>
#include <fpdfview.h>
+#include <vcl/filter/PDFiumLibrary.hxx>
+
using namespace ::com::sun::star;
namespace
@@ -44,6 +46,7 @@ class PdfExportTest : public test::BootstrapFixture, public unotest::MacrosTest
SvMemoryStream maMemory;
// Export the document as PDF, then parse it with PDFium.
void exportAndParse(const OUString& rURL, const utl::MediaDescriptor& rDescriptor);
+ std::shared_ptr<vcl::pdf::PDFium> mpPDFium;
public:
PdfExportTest();
@@ -141,19 +144,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_ClosePage(mpPdfPage);
FPDF_CloseDocument(mpPdfDocument);
- FPDF_DestroyLibrary();
if (mxComponent.is())
mxComponent->dispose();
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
index e1d5c5b9b890..b26b4157e54c 100644
--- a/vcl/source/filter/ipdf/pdfread.cxx
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -23,6 +23,8 @@
#include <vcl/bitmapaccess.hxx>
#include <unotools/ucbstreamhelper.hxx>
+#include <vcl/filter/PDFiumLibrary.hxx>
+
using namespace com::sun::star;
namespace
@@ -107,12 +109,7 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream,
else
{
// Downconvert to PDF-1.4.
- 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;
@@ -129,7 +126,6 @@ bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream,
return false;
FPDF_CloseDocument(pPdfDocument);
- FPDF_DestroyLibrary();
aWriter.m_aStream.Seek(STREAM_SEEK_TO_BEGIN);
rOutStream.WriteStream(aWriter.m_aStream);
@@ -163,12 +159,7 @@ size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<Bitmap>& rBi
const size_t nFirstPage, int nPages,
const double fResolutionDPI)
{
- 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();
// Load the buffer using pdfium.
FPDF_DOCUMENT pPdfDocument = FPDF_LoadMemDocument(pBuffer, nSize, /*password=*/nullptr);
@@ -217,7 +208,6 @@ size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<Bitmap>& rBi
}
FPDF_CloseDocument(pPdfDocument);
- FPDF_DestroyLibrary();
return rBitmaps.size();
}
@@ -314,12 +304,7 @@ size_t ImportPDFUnloaded(const OUString& rURL, std::vector<std::pair<Graphic, Si
std::shared_ptr<GfxLink> pGfxLink(std::make_shared<GfxLink>(
std::move(pGraphicContent), nGraphicContentSize, GfxLinkType::NativePdf));
- 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();
// Load the buffer using pdfium.
FPDF_DOCUMENT pPdfDocument
@@ -357,7 +342,6 @@ size_t ImportPDFUnloaded(const OUString& rURL, std::vector<std::pair<Graphic, Si
}
FPDF_CloseDocument(pPdfDocument);
- FPDF_DestroyLibrary();
return rGraphics.size();
#else
diff --git a/vcl/source/pdf/PDFiumLibrary.cxx b/vcl/source/pdf/PDFiumLibrary.cxx
new file mode 100644
index 000000000000..9d822c34642c
--- /dev/null
+++ b/vcl/source/pdf/PDFiumLibrary.cxx
@@ -0,0 +1,39 @@
+/* -*- 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 pdf
+} // end vcl
+
+#endif // HAVE_FEATURE_PDFIUM
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 9ce29950a971a88a785dd03f9bc07813b8248ba2
Author: Miklos Vajna <vmiklos at collabora.com>
AuthorDate: Fri Oct 16 18:15:21 2020 +0200
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Dec 21 10:07:06 2020 +0100
vcl pdf tokenizer: fix handling of dict -> array -> dict tokens
Needed to be able to parse the /Reference key of signatures.
(cherry picked from commit 056c1284d6a68525002c54bef10834cc135385db)
Conflicts:
vcl/qa/cppunit/filter/ipdf/ipdf.cxx
Change-Id: I6b81089a3f58a2de461ad92ca5a891c284f8686a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107935
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
diff --git a/vcl/CppunitTest_vcl_filter_ipdf.mk b/vcl/CppunitTest_vcl_filter_ipdf.mk
new file mode 100644
index 000000000000..403836ac781a
--- /dev/null
+++ b/vcl/CppunitTest_vcl_filter_ipdf.mk
@@ -0,0 +1,49 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# 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/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,vcl_filter_ipdf))
+
+$(eval $(call gb_CppunitTest_use_externals,vcl_filter_ipdf,\
+ boost_headers \
+ pdfium \
+))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,vcl_filter_ipdf, \
+ vcl/qa/cppunit/filter/ipdf/ipdf \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,vcl_filter_ipdf, \
+ comphelper \
+ cppu \
+ sal \
+ sfx \
+ svx \
+ test \
+ tl \
+ unotest \
+ utl \
+ vcl \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,vcl_filter_ipdf))
+
+$(eval $(call gb_CppunitTest_use_ure,vcl_filter_ipdf))
+$(eval $(call gb_CppunitTest_use_vcl,vcl_filter_ipdf))
+
+$(eval $(call gb_CppunitTest_use_rdb,vcl_filter_ipdf,services))
+
+$(eval $(call gb_CppunitTest_use_custom_headers,vcl_filter_ipdf,\
+ officecfg/registry \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,vcl_filter_ipdf))
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index a6a674bff685..0dd4eee01f69 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -181,6 +181,7 @@ $(eval $(call gb_Module_add_check_targets,vcl,\
ifneq (,$(filter PDFIUM,$(BUILD_TYPE)))
$(eval $(call gb_Module_add_check_targets,vcl,\
CppunitTest_vcl_pdfexport \
+ CppunitTest_vcl_filter_ipdf \
))
endif
diff --git a/vcl/qa/cppunit/filter/ipdf/data/dict-array-dict.pdf b/vcl/qa/cppunit/filter/ipdf/data/dict-array-dict.pdf
new file mode 100644
index 000000000000..73de3117b9a6
--- /dev/null
+++ b/vcl/qa/cppunit/filter/ipdf/data/dict-array-dict.pdf
@@ -0,0 +1,55 @@
+%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
+ /Key[<</InnerKey 42>>]
+>>
+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
+0000000251 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+491
+%%EOF
diff --git a/vcl/qa/cppunit/filter/ipdf/ipdf.cxx b/vcl/qa/cppunit/filter/ipdf/ipdf.cxx
new file mode 100644
index 000000000000..a66adcf923e0
--- /dev/null
+++ b/vcl/qa/cppunit/filter/ipdf/ipdf.cxx
@@ -0,0 +1,112 @@
+/* -*- 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 <test/bootstrapfixture.hxx>
+#include <unotest/macros_test.hxx>
+
+#include <prewin.h>
+#include <postwin.h>
+
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <osl/file.hxx>
+#include <unotools/tempfile.hxx>
+#include <sfx2/sfxbasemodel.hxx>
+#include <svx/svdview.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/objsh.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/filter/pdfdocument.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+char const DATA_DIRECTORY[] = "/vcl/qa/cppunit/filter/ipdf/data/";
+}
+
+/// Covers vcl/source/filter/ipdf/ fixes.
+class VclFilterIpdfTest : public test::BootstrapFixture, public unotest::MacrosTest
+{
+private:
+ uno::Reference<lang::XComponent> mxComponent;
+ uno::Reference<xml::crypto::XSEInitializer> mxSEInitializer;
+ uno::Reference<xml::crypto::XXMLSecurityContext> mxSecurityContext;
+
+public:
+ void setUp() override;
+ void tearDown() override;
+ uno::Reference<lang::XComponent>& getComponent() { return mxComponent; }
+ uno::Reference<xml::crypto::XXMLSecurityContext>& getSecurityContext()
+ {
+ return mxSecurityContext;
+ }
+};
+
+void VclFilterIpdfTest::setUp()
+{
+ test::BootstrapFixture::setUp();
+
+ uno::Reference<uno::XComponentContext> xComponentContext
+ = comphelper::getComponentContext(getMultiServiceFactory());
+ mxDesktop.set(frame::Desktop::create(xComponentContext));
+ mxSEInitializer = xml::crypto::SEInitializer::create(xComponentContext);
+ mxSecurityContext = mxSEInitializer->createSecurityContext(OUString());
+}
+
+void VclFilterIpdfTest::tearDown()
+{
+ if (mxComponent.is())
+ mxComponent->dispose();
+
+ test::BootstrapFixture::tearDown();
+}
+
+#ifndef CPPUNIT_TEST_FIXTURE
+#define CPPUNIT_TEST_FIXTURE(TestClass, TestName) \
+ class TestName : public TestClass \
+ { \
+ public: \
+ void TestBody(); \
+ CPPUNIT_TEST_SUITE(TestName); \
+ CPPUNIT_TEST(TestBody); \
+ CPPUNIT_TEST_SUITE_END(); \
+ }; \
+ CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
+ void TestName::TestBody()
+#endif
+CPPUNIT_TEST_FIXTURE(VclFilterIpdfTest, testDictArrayDict)
+{
+ // Load a file that has markup like this:
+ // 3 0 obj <<
+ // /Key[<</InnerKey 42>>]
+ // >>
+ OUString aSourceURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "dict-array-dict.pdf";
+ SvFileStream aFile(aSourceURL, StreamMode::READ);
+ vcl::filter::PDFDocument aDocument;
+ CPPUNIT_ASSERT(aDocument.Read(aFile));
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT(!aPages.empty());
+ vcl::filter::PDFObjectElement* pPage = aPages[0];
+ auto pKey = dynamic_cast<vcl::filter::PDFArrayElement*>(pPage->Lookup("Key"));
+
+ // Without the accompanying fix in place, this test would have failed, because the value of Key
+ // was a dictionary element, not an array element.
+ CPPUNIT_ASSERT(pKey);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
index ecbf948f1292..6851bef19b91 100644
--- a/vcl/source/filter/ipdf/pdfdocument.cxx
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -2191,8 +2191,17 @@ size_t PDFDictionaryElement::Parse(const std::vector< std::unique_ptr<PDFElement
if (nexti >= i) // ensure we go forwards and not endlessly loop
{
i = nexti;
- rDictionary[aName] = pDictionary;
- aName.clear();
+ if (pArray)
+ {
+ // Dictionary value inside an array.
+ pArray->PushBack(pDictionary);
+ }
+ else
+ {
+ // Dictionary toplevel value.
+ rDictionary[aName] = pDictionary;
+ aName.clear();
+ }
}
}
}
commit a025f3574639f41323c0df55f2e9fd1761cbd6ad
Author: Michael Stahl <MichaelSt at cib.de>
AuthorDate: Thu Dec 3 17:42:18 2020 +0100
Commit: Miklos Vajna <vmiklos at collabora.com>
CommitDate: Mon Dec 21 10:07:06 2020 +0100
pdfium: MSVC 2015 build
(cherry picked from commit 7ac3af8c89af7d481c027df75026f390258e6e5a)
Change-Id: I5ea89841fafe3ea96fa256e91151eceb8235731e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107871
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
diff --git a/external/pdfium/UnpackedTarball_pdfium.mk b/external/pdfium/UnpackedTarball_pdfium.mk
index f4643376cee0..8b12e494f94b 100644
--- a/external/pdfium/UnpackedTarball_pdfium.mk
+++ b/external/pdfium/UnpackedTarball_pdfium.mk
@@ -14,6 +14,9 @@ pdfium_patches += build.patch.1
# Avoids Windows 8 build dependency.
pdfium_patches += windows7.patch.1
pdfium_patches += c++20-comparison.patch
+ifeq (MSC,$(COM))
+pdfium_patches += pdfium4137-numerics.patch.3 msvc2015.patch.1
+endif
$(eval $(call gb_UnpackedTarball_UnpackedTarball,pdfium))
diff --git a/external/pdfium/msvc2015.patch.1 b/external/pdfium/msvc2015.patch.1
new file mode 100644
index 000000000000..36cb5332c7b0
--- /dev/null
+++ b/external/pdfium/msvc2015.patch.1
@@ -0,0 +1,202 @@
+Fix MSVC 2015 build
+
+--- pdfium/third_party/base/optional.h.orig 2020-10-26 19:26:04.000000000 +0100
++++ pdfium/third_party/base/optional.h 2020-12-03 16:00:54.879883100 +0100
+@@ -36,7 +36,7 @@
+ struct OptionalStorageBase {
+ // Provide non-defaulted default ctor to make sure it's not deleted by
+ // non-trivial T::T() in the union.
+- constexpr OptionalStorageBase() : dummy_() {}
++ OptionalStorageBase() : dummy_() {}
+
+ template <class... Args>
+ constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
+@@ -88,7 +88,7 @@
+ struct OptionalStorageBase<T, true /* trivially destructible */> {
+ // Provide non-defaulted default ctor to make sure it's not deleted by
+ // non-trivial T::T() in the union.
+- constexpr OptionalStorageBase() : dummy_() {}
++ OptionalStorageBase() : dummy_() {}
+
+ template <class... Args>
+ constexpr explicit OptionalStorageBase(in_place_t, Args&&... args)
+@@ -607,32 +607,32 @@
+ return *this;
+ }
+
+- constexpr const T* operator->() const {
++ const T* operator->() const {
+ CHECK(storage_.is_populated_);
+ return &storage_.value_;
+ }
+
+- constexpr T* operator->() {
++ T* operator->() {
+ CHECK(storage_.is_populated_);
+ return &storage_.value_;
+ }
+
+- constexpr const T& operator*() const & {
++ const T& operator*() const & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+- constexpr T& operator*() & {
++ T& operator*() & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+- constexpr const T&& operator*() const && {
++ const T&& operator*() const && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+
+- constexpr T&& operator*() && {
++ T&& operator*() && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+@@ -641,22 +641,22 @@
+
+ constexpr bool has_value() const { return storage_.is_populated_; }
+
+- constexpr T& value() & {
++ T& value() & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+- constexpr const T& value() const & {
++ const T& value() const & {
+ CHECK(storage_.is_populated_);
+ return storage_.value_;
+ }
+
+- constexpr T&& value() && {
++ T&& value() && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+
+- constexpr const T&& value() const && {
++ const T&& value() const && {
+ CHECK(storage_.is_populated_);
+ return std::move(storage_.value_);
+ }
+--- pdfium/third_party/base/span.h.orig 2020-10-26 19:26:04.000000000 +0100
++++ pdfium/third_party/base/span.h 2020-12-03 16:28:15.642138100 +0100
+@@ -193,7 +193,7 @@
+
+ // TODO(dcheng): Implement construction from a |begin| and |end| pointer.
+ template <size_t N>
+- constexpr span(T (&array)[N]) noexcept : span(array, N) {}
++ span(T (&array)[N]) noexcept : span(array, N) {}
+ // TODO(dcheng): Implement construction from std::array.
+ // Conversion from a container that provides |T* data()| and |integral_type
+ // size()|.
+--- pdfium/core/fpdfapi/page/cpdf_colorspace.cpp.orig 2020-12-03 16:54:15.514659400 +0100
++++ pdfium/core/fpdfapi/page/cpdf_colorspace.cpp 2020-12-03 16:38:52.167650200 +0100
+@@ -905,7 +905,7 @@
+ float R;
+ float G;
+ float B;
+- GetRGB(lab, &R, &G, &B);
++ GetRGB(pdfium::span<float>(lab), &R, &G, &B);
+ pDestBuf[0] = static_cast<int32_t>(B * 255);
+ pDestBuf[1] = static_cast<int32_t>(G * 255);
+ pDestBuf[2] = static_cast<int32_t>(R * 255);
+--- pdfium/core/fpdfapi/page/cpdf_meshstream.cpp.orig 2020-12-03 16:54:09.233498800 +0100
++++ pdfium/core/fpdfapi/page/cpdf_meshstream.cpp 2020-12-03 16:41:29.173766500 +0100
+@@ -209,7 +209,7 @@
+ func->Call(color_value, 1, result, &nResults);
+ }
+
+- m_pCS->GetRGB(result, &r, &g, &b);
++ m_pCS->GetRGB(pdfium::span<float>(result), &r, &g, &b);
+ return std::tuple<float, float, float>(r, g, b);
+ }
+
+--- pdfium/core/fpdfapi/parser/cpdf_security_handler.cpp.orig 2020-12-03 16:53:56.077095400 +0100
++++ pdfium/core/fpdfapi/parser/cpdf_security_handler.cpp 2020-12-03 16:44:23.951334200 +0100
+@@ -481,7 +481,7 @@
+ uint8_t passcode[32];
+ GetPassCode(owner_password, passcode);
+ uint8_t digest[16];
+- CRYPT_MD5Generate(passcode, digest);
++ CRYPT_MD5Generate(pdfium::span<uint8_t>(passcode), digest);
+ if (m_Revision >= 3) {
+ for (uint32_t i = 0; i < 50; i++)
+ CRYPT_MD5Generate(digest, digest);
+@@ -570,10 +570,10 @@
+ uint8_t passcode[32];
+ GetPassCode(owner_password_copy, passcode);
+ uint8_t digest[16];
+- CRYPT_MD5Generate(passcode, digest);
++ CRYPT_MD5Generate(pdfium::span<uint8_t>(passcode), digest);
+ if (m_Revision >= 3) {
+ for (uint32_t i = 0; i < 50; i++)
+- CRYPT_MD5Generate(digest, digest);
++ CRYPT_MD5Generate(pdfium::span<uint8_t>(digest), digest);
+ }
+ uint8_t enckey[32];
+ memcpy(enckey, digest, key_len);
+--- pdfium/core/fpdfapi/page/cpdf_dib.cpp.orig 2020-12-03 16:53:44.548444600 +0100
++++ pdfium/core/fpdfapi/page/cpdf_dib.cpp 2020-12-03 16:49:11.937584700 +0100
+@@ -874,7 +874,7 @@
+ color_values[0] += m_CompData[0].m_DecodeStep;
+ color_values[1] += m_CompData[0].m_DecodeStep;
+ color_values[2] += m_CompData[0].m_DecodeStep;
+- m_pColorSpace->GetRGB(color_values, &R, &G, &B);
++ m_pColorSpace->GetRGB(pdfium::span<float>(color_values), &R, &G, &B);
+ FX_ARGB argb1 = ArgbEncode(255, FXSYS_roundf(R * 255),
+ FXSYS_roundf(G * 255), FXSYS_roundf(B * 255));
+ if (argb0 != 0xFF000000 || argb1 != 0xFFFFFFFF) {
+--- pdfium/third_party/base/allocator/partition_allocator/partition_alloc.cc.orig 2020-12-03 17:09:02.887283800 +0100
++++ pdfium/third_party/base/allocator/partition_allocator/partition_alloc.cc 2020-12-03 17:07:22.198993800 +0100
+@@ -67,12 +67,12 @@
+ // Chained hooks are not supported. Registering a non-null hook when a
+ // non-null hook is already registered indicates somebody is trying to
+ // overwrite a hook.
+- CHECK((!allocation_observer_hook_ && !free_observer_hook_) ||
++ CHECK((!allocation_observer_hook_.load() && !free_observer_hook_.load()) ||
+ (!alloc_hook && !free_hook));
+ allocation_observer_hook_ = alloc_hook;
+ free_observer_hook_ = free_hook;
+
+- hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
++ hooks_enabled_ = allocation_observer_hook_.load() || allocation_override_hook_.load();
+ }
+
+ void PartitionAllocHooks::SetOverrideHooks(AllocationOverrideHook* alloc_hook,
+@@ -80,14 +80,14 @@
+ ReallocOverrideHook realloc_hook) {
+ subtle::SpinLock::Guard guard(set_hooks_lock_);
+
+- CHECK((!allocation_override_hook_ && !free_override_hook_ &&
+- !realloc_override_hook_) ||
++ CHECK((!allocation_override_hook_.load() && !free_override_hook_.load() &&
++ !realloc_override_hook_.load()) ||
+ (!alloc_hook && !free_hook && !realloc_hook));
+ allocation_override_hook_ = alloc_hook;
+ free_override_hook_ = free_hook;
+ realloc_override_hook_ = realloc_hook;
+
+- hooks_enabled_ = allocation_observer_hook_ || allocation_override_hook_;
++ hooks_enabled_ = allocation_observer_hook_.load() || allocation_override_hook_.load();
+ }
+
+ void PartitionAllocHooks::AllocationObserverHookIfEnabled(
+--- pdfium/third_party/base/allocator/partition_allocator/partition_page.h.orig 2020-12-03 17:13:56.944624000 +0100
++++ pdfium/third_party/base/allocator/partition_allocator/partition_page.h 2020-12-03 17:13:34.385932300 +0100
+@@ -25,6 +25,8 @@
+ struct DeferredUnmap {
+ void* ptr = nullptr;
+ size_t size = 0;
++ DeferredUnmap(void* const p, size_t const s) : ptr(p), size(s) {}
++ DeferredUnmap() = default;
+ // In most cases there is no page to unmap and ptr == nullptr. This function
+ // is inlined to avoid the overhead of a function call in the common case.
+ ALWAYS_INLINE void Run();
diff --git a/external/pdfium/pdfium4137-numerics.patch.3 b/external/pdfium/pdfium4137-numerics.patch.3
new file mode 100644
index 000000000000..7d27ccd7f3cb
--- /dev/null
+++ b/external/pdfium/pdfium4137-numerics.patch.3
@@ -0,0 +1,3364 @@
+Restore numerics headers from release 4137
+
+diff -Naur workdir/UnpackedTarball/pdfium/third_party/base/numerics/checked_math.h workdir/UnpackedTarball/pdfium.4137/third_party/base/numerics/checked_math.h
+--- workdir/UnpackedTarball/pdfium/third_party/base/numerics/checked_math.h 2020-10-26 19:26:04.000000000 +0100
++++ workdir/UnpackedTarball/pdfium.4137/third_party/base/numerics/checked_math.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,395 +0,0 @@
+-// Copyright 2017 The Chromium Authors. All rights reserved.
+-// Use of this source code is governed by a BSD-style license that can be
+-// found in the LICENSE file.
+-
+-#ifndef THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_H_
+-#define THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_H_
+-
+-#include <stddef.h>
+-
+-#include <limits>
+-#include <type_traits>
+-
+-#include "third_party/base/numerics/checked_math_impl.h"
+-
+-namespace pdfium {
+-namespace base {
+-namespace internal {
+-
+-template <typename T>
+-class CheckedNumeric {
+- static_assert(std::is_arithmetic<T>::value,
+- "CheckedNumeric<T>: T must be a numeric type.");
+-
+- public:
+- using type = T;
+-
+- constexpr CheckedNumeric() = default;
+-
+- // Copy constructor.
+- template <typename Src>
+- constexpr CheckedNumeric(const CheckedNumeric<Src>& rhs)
+- : state_(rhs.state_.value(), rhs.IsValid()) {}
+-
+- template <typename Src>
+- friend class CheckedNumeric;
+-
+- // This is not an explicit constructor because we implicitly upgrade regular
+- // numerics to CheckedNumerics to make them easier to use.
+- template <typename Src>
+- constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit)
+- : state_(value) {
+- static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric.");
+- }
+-
+- // This is not an explicit constructor because we want a seamless conversion
+- // from StrictNumeric types.
+- template <typename Src>
+- constexpr CheckedNumeric(
+- StrictNumeric<Src> value) // NOLINT(runtime/explicit)
+- : state_(static_cast<Src>(value)) {}
+-
+- // IsValid() - The public API to test if a CheckedNumeric is currently valid.
+- // A range checked destination type can be supplied using the Dst template
+- // parameter.
+- template <typename Dst = T>
+- constexpr bool IsValid() const {
+- return state_.is_valid() &&
+- IsValueInRangeForNumericType<Dst>(state_.value());
+- }
+-
+- // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid
+- // and is within the range supported by the destination type. Returns true if
+- // successful and false otherwise.
+- template <typename Dst>
+-#if defined(__clang__) || defined(__GNUC__)
+- __attribute__((warn_unused_result))
+-#elif defined(_MSC_VER)
+- _Check_return_
+-#endif
+- constexpr bool
+- AssignIfValid(Dst* result) const {
+- return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+- ? ((*result = static_cast<Dst>(state_.value())), true)
+- : false;
+- }
+-
+- // ValueOrDie() - The primary accessor for the underlying value. If the
+- // current state is not valid it will CHECK and crash.
+- // A range checked destination type can be supplied using the Dst template
+- // parameter, which will trigger a CHECK if the value is not in bounds for
+- // the destination.
+- // The CHECK behavior can be overridden by supplying a handler as a
+- // template parameter, for test code, etc. However, the handler cannot access
+- // the underlying value, and it is not available through other means.
+- template <typename Dst = T, class CheckHandler = CheckOnFailure>
+- constexpr StrictNumeric<Dst> ValueOrDie() const {
+- return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+- ? static_cast<Dst>(state_.value())
+- : CheckHandler::template HandleFailure<Dst>();
+- }
+-
+- // ValueOrDefault(T default_value) - A convenience method that returns the
+- // current value if the state is valid, and the supplied default_value for
+- // any other state.
+- // A range checked destination type can be supplied using the Dst template
+- // parameter. WARNING: This function may fail to compile or CHECK at runtime
+- // if the supplied default_value is not within range of the destination type.
+- template <typename Dst = T, typename Src>
+- constexpr StrictNumeric<Dst> ValueOrDefault(const Src default_value) const {
+- return BASE_NUMERICS_LIKELY(IsValid<Dst>())
+- ? static_cast<Dst>(state_.value())
+- : checked_cast<Dst>(default_value);
+- }
+-
+- // Returns a checked numeric of the specified type, cast from the current
+- // CheckedNumeric. If the current state is invalid or the destination cannot
+- // represent the result then the returned CheckedNumeric will be invalid.
+- template <typename Dst>
+- constexpr CheckedNumeric<typename UnderlyingType<Dst>::type> Cast() const {
+- return *this;
+- }
+-
+- // This friend method is available solely for providing more detailed logging
+- // in the the tests. Do not implement it in production code, because the
+- // underlying values may change at any time.
+- template <typename U>
+- friend U GetNumericValueForTest(const CheckedNumeric<U>& src);
+-
+- // Prototypes for the supported arithmetic operator overloads.
+- template <typename Src>
+- constexpr CheckedNumeric& operator+=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator-=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator*=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator/=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator%=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator<<=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator>>=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator&=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator|=(const Src rhs);
+- template <typename Src>
+- constexpr CheckedNumeric& operator^=(const Src rhs);
+-
+- constexpr CheckedNumeric operator-() const {
+- // The negation of two's complement int min is int min, so we simply
+- // check for that in the constexpr case.
+- // We use an optimized code path for a known run-time variable.
+- return MustTreatAsConstexpr(state_.value()) || !std::is_signed<T>::value ||
+- std::is_floating_point<T>::value
+- ? CheckedNumeric<T>(
+- NegateWrapper(state_.value()),
+- IsValid() && (!std::is_signed<T>::value ||
+- std::is_floating_point<T>::value ||
+- NegateWrapper(state_.value()) !=
+- std::numeric_limits<T>::lowest()))
+- : FastRuntimeNegate();
+- }
+-
+- constexpr CheckedNumeric operator~() const {
+- return CheckedNumeric<decltype(InvertWrapper(T()))>(
+- InvertWrapper(state_.value()), IsValid());
+- }
+-
+- constexpr CheckedNumeric Abs() const {
+- return !IsValueNegative(state_.value()) ? *this : -*this;
+- }
+-
+- template <typename U>
+- constexpr CheckedNumeric<typename MathWrapper<CheckedMaxOp, T, U>::type> Max(
+- const U rhs) const {
+- using R = typename UnderlyingType<U>::type;
+- using result_type = typename MathWrapper<CheckedMaxOp, T, U>::type;
+- // TODO(jschuh): This can be converted to the MathOp version and remain
+- // constexpr once we have C++14 support.
+- return CheckedNumeric<result_type>(
+- static_cast<result_type>(
+- IsGreater<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
+- ? state_.value()
+- : Wrapper<U>::value(rhs)),
+- state_.is_valid() && Wrapper<U>::is_valid(rhs));
+- }
+-
+- template <typename U>
+- constexpr CheckedNumeric<typename MathWrapper<CheckedMinOp, T, U>::type> Min(
+- const U rhs) const {
+- using R = typename UnderlyingType<U>::type;
+- using result_type = typename MathWrapper<CheckedMinOp, T, U>::type;
+- // TODO(jschuh): This can be converted to the MathOp version and remain
+- // constexpr once we have C++14 support.
+- return CheckedNumeric<result_type>(
+- static_cast<result_type>(
+- IsLess<T, R>::Test(state_.value(), Wrapper<U>::value(rhs))
+- ? state_.value()
+- : Wrapper<U>::value(rhs)),
+- state_.is_valid() && Wrapper<U>::is_valid(rhs));
+- }
+-
+- // This function is available only for integral types. It returns an unsigned
+- // integer of the same width as the source type, containing the absolute value
+- // of the source, and properly handling signed min.
+- constexpr CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>
+- UnsignedAbs() const {
+- return CheckedNumeric<typename UnsignedOrFloatForSize<T>::type>(
+- SafeUnsignedAbs(state_.value()), state_.is_valid());
+- }
+-
+- constexpr CheckedNumeric& operator++() {
+- *this += 1;
+- return *this;
+- }
+-
+- constexpr CheckedNumeric operator++(int) {
+- CheckedNumeric value = *this;
+- *this += 1;
+- return value;
+- }
+-
+- constexpr CheckedNumeric& operator--() {
+- *this -= 1;
+- return *this;
+- }
+-
+- constexpr CheckedNumeric operator--(int) {
+- CheckedNumeric value = *this;
+- *this -= 1;
+- return value;
+- }
+-
+- // These perform the actual math operations on the CheckedNumerics.
+- // Binary arithmetic operations.
+- template <template <typename, typename, typename> class M,
+- typename L,
+- typename R>
+- static constexpr CheckedNumeric MathOp(const L lhs, const R rhs) {
+- using Math = typename MathWrapper<M, L, R>::math;
+- T result = 0;
+- bool is_valid =
+- Wrapper<L>::is_valid(lhs) && Wrapper<R>::is_valid(rhs) &&
+- Math::Do(Wrapper<L>::value(lhs), Wrapper<R>::value(rhs), &result);
+- return CheckedNumeric<T>(result, is_valid);
+- }
+-
+- // Assignment arithmetic operations.
+- template <template <typename, typename, typename> class M, typename R>
+- constexpr CheckedNumeric& MathOp(const R rhs) {
+- using Math = typename MathWrapper<M, T, R>::math;
+- T result = 0; // Using T as the destination saves a range check.
+- bool is_valid = state_.is_valid() && Wrapper<R>::is_valid(rhs) &&
+- Math::Do(state_.value(), Wrapper<R>::value(rhs), &result);
+- *this = CheckedNumeric<T>(result, is_valid);
+- return *this;
+- }
+-
+- private:
+- CheckedNumericState<T> state_;
+-
+- CheckedNumeric FastRuntimeNegate() const {
+- T result;
+- bool success = CheckedSubOp<T, T>::Do(T(0), state_.value(), &result);
+- return CheckedNumeric<T>(result, IsValid() && success);
+- }
+-
+- template <typename Src>
+- constexpr CheckedNumeric(Src value, bool is_valid)
+- : state_(value, is_valid) {}
+-
+- // These wrappers allow us to handle state the same way for both
+- // CheckedNumeric and POD arithmetic types.
+- template <typename Src>
+- struct Wrapper {
+- static constexpr bool is_valid(Src) { return true; }
+- static constexpr Src value(Src value) { return value; }
+- };
+-
+- template <typename Src>
+- struct Wrapper<CheckedNumeric<Src>> {
+- static constexpr bool is_valid(const CheckedNumeric<Src> v) {
+- return v.IsValid();
+- }
+- static constexpr Src value(const CheckedNumeric<Src> v) {
+- return v.state_.value();
+- }
+- };
+-
+- template <typename Src>
+- struct Wrapper<StrictNumeric<Src>> {
+- static constexpr bool is_valid(const StrictNumeric<Src>) { return true; }
+- static constexpr Src value(const StrictNumeric<Src> v) {
+- return static_cast<Src>(v);
+- }
+- };
+-};
+-
+-// Convenience functions to avoid the ugly template disambiguator syntax.
+-template <typename Dst, typename Src>
+-constexpr bool IsValidForType(const CheckedNumeric<Src> value) {
+- return value.template IsValid<Dst>();
+-}
+-
+-template <typename Dst, typename Src>
+-constexpr StrictNumeric<Dst> ValueOrDieForType(
+- const CheckedNumeric<Src> value) {
+- return value.template ValueOrDie<Dst>();
+-}
+-
+-template <typename Dst, typename Src, typename Default>
+-constexpr StrictNumeric<Dst> ValueOrDefaultForType(
+- const CheckedNumeric<Src> value,
+- const Default default_value) {
+- return value.template ValueOrDefault<Dst>(default_value);
+-}
+-
+-// Convience wrapper to return a new CheckedNumeric from the provided arithmetic
+-// or CheckedNumericType.
+-template <typename T>
+-constexpr CheckedNumeric<typename UnderlyingType<T>::type> MakeCheckedNum(
+- const T value) {
+- return value;
+-}
+-
+-// These implement the variadic wrapper for the math operations.
+-template <template <typename, typename, typename> class M,
+- typename L,
+- typename R>
+-constexpr CheckedNumeric<typename MathWrapper<M, L, R>::type> CheckMathOp(
+- const L lhs,
+- const R rhs) {
+- using Math = typename MathWrapper<M, L, R>::math;
+- return CheckedNumeric<typename Math::result_type>::template MathOp<M>(lhs,
+- rhs);
+-}
+-
+-// General purpose wrapper template for arithmetic operations.
+-template <template <typename, typename, typename> class M,
+- typename L,
+- typename R,
+- typename... Args>
+-constexpr CheckedNumeric<typename ResultType<M, L, R, Args...>::type>
+-CheckMathOp(const L lhs, const R rhs, const Args... args) {
+- return CheckMathOp<M>(CheckMathOp<M>(lhs, rhs), args...);
+-}
+-
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Add, +, +=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Sub, -, -=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mul, *, *=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Div, /, /=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Mod, %, %=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Lsh, <<, <<=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Rsh, >>, >>=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, And, &, &=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Or, |, |=)
+-BASE_NUMERIC_ARITHMETIC_OPERATORS(Checked, Check, Xor, ^, ^=)
+-BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Max)
+-BASE_NUMERIC_ARITHMETIC_VARIADIC(Checked, Check, Min)
+-
+-// These are some extra StrictNumeric operators to support simple pointer
+-// arithmetic with our result types. Since wrapping on a pointer is always
+-// bad, we trigger the CHECK condition here.
+-template <typename L, typename R>
+-L* operator+(L* lhs, const StrictNumeric<R> rhs) {
+- uintptr_t result = CheckAdd(reinterpret_cast<uintptr_t>(lhs),
+- CheckMul(sizeof(L), static_cast<R>(rhs)))
+- .template ValueOrDie<uintptr_t>();
+- return reinterpret_cast<L*>(result);
+-}
+-
+-template <typename L, typename R>
+-L* operator-(L* lhs, const StrictNumeric<R> rhs) {
+- uintptr_t result = CheckSub(reinterpret_cast<uintptr_t>(lhs),
+- CheckMul(sizeof(L), static_cast<R>(rhs)))
+- .template ValueOrDie<uintptr_t>();
+- return reinterpret_cast<L*>(result);
+-}
+-
+-} // namespace internal
+-
+-using internal::CheckedNumeric;
+-using internal::IsValidForType;
+-using internal::ValueOrDieForType;
+-using internal::ValueOrDefaultForType;
+-using internal::MakeCheckedNum;
+-using internal::CheckMax;
+-using internal::CheckMin;
+-using internal::CheckAdd;
+-using internal::CheckSub;
+-using internal::CheckMul;
+-using internal::CheckDiv;
+-using internal::CheckMod;
+-using internal::CheckLsh;
+-using internal::CheckRsh;
+-using internal::CheckAnd;
+-using internal::CheckOr;
+-using internal::CheckXor;
+-
+-} // namespace base
+-} // namespace pdfium
+-
+-#endif // THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_H_
+diff -Naur workdir/UnpackedTarball/pdfium/third_party/base/numerics/checked_math_impl.h workdir/UnpackedTarball/pdfium.4137/third_party/base/numerics/checked_math_impl.h
+--- workdir/UnpackedTarball/pdfium/third_party/base/numerics/checked_math_impl.h 2020-10-26 19:26:04.000000000 +0100
++++ workdir/UnpackedTarball/pdfium.4137/third_party/base/numerics/checked_math_impl.h 1970-01-01 01:00:00.000000000 +0100
+@@ -1,579 +0,0 @@
+-// Copyright 2017 The Chromium Authors. All rights reserved.
+-// Use of this source code is governed by a BSD-style license that can be
+-// found in the LICENSE file.
+-
+-#ifndef THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_IMPL_H_
+-#define THIRD_PARTY_BASE_NUMERICS_CHECKED_MATH_IMPL_H_
+-
+-#include <stddef.h>
+-#include <stdint.h>
+-
+-#include <climits>
+-#include <cmath>
+-#include <cstdlib>
+-#include <limits>
+-#include <type_traits>
+-
+-#include "third_party/base/numerics/safe_conversions.h"
+-#include "third_party/base/numerics/safe_math_shared_impl.h"
+-
+-namespace pdfium {
+-namespace base {
+-namespace internal {
+-
+-template <typename T>
+-constexpr bool CheckedAddImpl(T x, T y, T* result) {
+- static_assert(std::is_integral<T>::value, "Type must be integral");
+- // Since the value of x+y is undefined if we have a signed type, we compute
+- // it using the unsigned type of the same size.
+- using UnsignedDst = typename std::make_unsigned<T>::type;
+- using SignedDst = typename std::make_signed<T>::type;
+- UnsignedDst ux = static_cast<UnsignedDst>(x);
+- UnsignedDst uy = static_cast<UnsignedDst>(y);
+- UnsignedDst uresult = static_cast<UnsignedDst>(ux + uy);
+- *result = static_cast<T>(uresult);
+- // Addition is valid if the sign of (x + y) is equal to either that of x or
+- // that of y.
+- return (std::is_signed<T>::value)
+- ? static_cast<SignedDst>((uresult ^ ux) & (uresult ^ uy)) >= 0
+- : uresult >= uy; // Unsigned is either valid or underflow.
+-}
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedAddOp {};
+-
+-template <typename T, typename U>
+-struct CheckedAddOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename MaxExponentPromotion<T, U>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- // TODO(jschuh) Make this "constexpr if" once we're C++17.
+- if (CheckedAddFastOp<T, U>::is_supported)
+- return CheckedAddFastOp<T, U>::Do(x, y, result);
+-
+- // Double the underlying type up to a full machine word.
+- using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+- using Promotion =
+- typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+- IntegerBitsPlusSign<intptr_t>::value),
+- typename BigEnoughPromotion<T, U>::type,
+- FastPromotion>::type;
+- // Fail if either operand is out of range for the promoted type.
+- // TODO(jschuh): This could be made to work for a broader range of values.
+- if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+- !IsValueInRangeForNumericType<Promotion>(y))) {
+- return false;
+- }
+-
+- Promotion presult = {};
+- bool is_valid = true;
+- if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+- presult = static_cast<Promotion>(x) + static_cast<Promotion>(y);
+- } else {
+- is_valid = CheckedAddImpl(static_cast<Promotion>(x),
+- static_cast<Promotion>(y), &presult);
+- }
+- *result = static_cast<V>(presult);
+- return is_valid && IsValueInRangeForNumericType<V>(presult);
+- }
+-};
+-
+-template <typename T>
+-constexpr bool CheckedSubImpl(T x, T y, T* result) {
+- static_assert(std::is_integral<T>::value, "Type must be integral");
+- // Since the value of x+y is undefined if we have a signed type, we compute
+- // it using the unsigned type of the same size.
+- using UnsignedDst = typename std::make_unsigned<T>::type;
+- using SignedDst = typename std::make_signed<T>::type;
+- UnsignedDst ux = static_cast<UnsignedDst>(x);
+- UnsignedDst uy = static_cast<UnsignedDst>(y);
+- UnsignedDst uresult = static_cast<UnsignedDst>(ux - uy);
+- *result = static_cast<T>(uresult);
+- // Subtraction is valid if either x and y have same sign, or (x-y) and x have
+- // the same sign.
+- return (std::is_signed<T>::value)
+- ? static_cast<SignedDst>((uresult ^ ux) & (ux ^ uy)) >= 0
+- : x >= y;
+-}
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedSubOp {};
+-
+-template <typename T, typename U>
+-struct CheckedSubOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename MaxExponentPromotion<T, U>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- // TODO(jschuh) Make this "constexpr if" once we're C++17.
+- if (CheckedSubFastOp<T, U>::is_supported)
+- return CheckedSubFastOp<T, U>::Do(x, y, result);
+-
+- // Double the underlying type up to a full machine word.
+- using FastPromotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+- using Promotion =
+- typename std::conditional<(IntegerBitsPlusSign<FastPromotion>::value >
+- IntegerBitsPlusSign<intptr_t>::value),
+- typename BigEnoughPromotion<T, U>::type,
+- FastPromotion>::type;
+- // Fail if either operand is out of range for the promoted type.
+- // TODO(jschuh): This could be made to work for a broader range of values.
+- if (BASE_NUMERICS_UNLIKELY(!IsValueInRangeForNumericType<Promotion>(x) ||
+- !IsValueInRangeForNumericType<Promotion>(y))) {
+- return false;
+- }
+-
+- Promotion presult = {};
+- bool is_valid = true;
+- if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+- presult = static_cast<Promotion>(x) - static_cast<Promotion>(y);
+- } else {
+- is_valid = CheckedSubImpl(static_cast<Promotion>(x),
+- static_cast<Promotion>(y), &presult);
+- }
+- *result = static_cast<V>(presult);
+- return is_valid && IsValueInRangeForNumericType<V>(presult);
+- }
+-};
+-
+-template <typename T>
+-constexpr bool CheckedMulImpl(T x, T y, T* result) {
+- static_assert(std::is_integral<T>::value, "Type must be integral");
+- // Since the value of x*y is potentially undefined if we have a signed type,
+- // we compute it using the unsigned type of the same size.
+- using UnsignedDst = typename std::make_unsigned<T>::type;
+- using SignedDst = typename std::make_signed<T>::type;
+- const UnsignedDst ux = SafeUnsignedAbs(x);
+- const UnsignedDst uy = SafeUnsignedAbs(y);
+- UnsignedDst uresult = static_cast<UnsignedDst>(ux * uy);
+- const bool is_negative =
+- std::is_signed<T>::value && static_cast<SignedDst>(x ^ y) < 0;
+- *result = is_negative ? 0 - uresult : uresult;
+- // We have a fast out for unsigned identity or zero on the second operand.
+- // After that it's an unsigned overflow check on the absolute value, with
+- // a +1 bound for a negative result.
+- return uy <= UnsignedDst(!std::is_signed<T>::value || is_negative) ||
+- ux <= (std::numeric_limits<T>::max() + UnsignedDst(is_negative)) / uy;
+-}
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedMulOp {};
+-
+-template <typename T, typename U>
+-struct CheckedMulOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename MaxExponentPromotion<T, U>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- // TODO(jschuh) Make this "constexpr if" once we're C++17.
+- if (CheckedMulFastOp<T, U>::is_supported)
+- return CheckedMulFastOp<T, U>::Do(x, y, result);
+-
+- using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type;
+- // Verify the destination type can hold the result (always true for 0).
+- if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+- !IsValueInRangeForNumericType<Promotion>(y)) &&
+- x && y)) {
+- return false;
+- }
+-
+- Promotion presult = {};
+- bool is_valid = true;
+- if (CheckedMulFastOp<Promotion, Promotion>::is_supported) {
+- // The fast op may be available with the promoted type.
+- is_valid = CheckedMulFastOp<Promotion, Promotion>::Do(x, y, &presult);
+- } else if (IsIntegerArithmeticSafe<Promotion, T, U>::value) {
+- presult = static_cast<Promotion>(x) * static_cast<Promotion>(y);
+- } else {
+- is_valid = CheckedMulImpl(static_cast<Promotion>(x),
+- static_cast<Promotion>(y), &presult);
+- }
+- *result = static_cast<V>(presult);
+- return is_valid && IsValueInRangeForNumericType<V>(presult);
+- }
+-};
+-
+-// Division just requires a check for a zero denominator or an invalid negation
+-// on signed min/-1.
+-template <typename T, typename U, class Enable = void>
+-struct CheckedDivOp {};
+-
+-template <typename T, typename U>
+-struct CheckedDivOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename MaxExponentPromotion<T, U>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- if (BASE_NUMERICS_UNLIKELY(!y))
+- return false;
+-
+- // The overflow check can be compiled away if we don't have the exact
+- // combination of types needed to trigger this case.
+- using Promotion = typename BigEnoughPromotion<T, U>::type;
+- if (BASE_NUMERICS_UNLIKELY(
+- (std::is_signed<T>::value && std::is_signed<U>::value &&
+- IsTypeInRangeForNumericType<T, Promotion>::value &&
+- static_cast<Promotion>(x) ==
+- std::numeric_limits<Promotion>::lowest() &&
+- y == static_cast<U>(-1)))) {
+- return false;
+- }
+-
+- // This branch always compiles away if the above branch wasn't removed.
+- if (BASE_NUMERICS_UNLIKELY((!IsValueInRangeForNumericType<Promotion>(x) ||
+- !IsValueInRangeForNumericType<Promotion>(y)) &&
+- x)) {
+- return false;
+- }
+-
+- Promotion presult = Promotion(x) / Promotion(y);
+- *result = static_cast<V>(presult);
+- return IsValueInRangeForNumericType<V>(presult);
+- }
+-};
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedModOp {};
+-
+-template <typename T, typename U>
+-struct CheckedModOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename MaxExponentPromotion<T, U>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- if (BASE_NUMERICS_UNLIKELY(!y))
+- return false;
+-
+- using Promotion = typename BigEnoughPromotion<T, U>::type;
+- if (BASE_NUMERICS_UNLIKELY(
+- (std::is_signed<T>::value && std::is_signed<U>::value &&
+- IsTypeInRangeForNumericType<T, Promotion>::value &&
+- static_cast<Promotion>(x) ==
+- std::numeric_limits<Promotion>::lowest() &&
+- y == static_cast<U>(-1)))) {
+- *result = 0;
+- return true;
+- }
+-
+- Promotion presult = static_cast<Promotion>(x) % static_cast<Promotion>(y);
+- *result = static_cast<Promotion>(presult);
+- return IsValueInRangeForNumericType<V>(presult);
+- }
+-};
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedLshOp {};
+-
+-// Left shift. Shifts less than 0 or greater than or equal to the number
+-// of bits in the promoted type are undefined. Shifts of negative values
+-// are undefined. Otherwise it is defined when the result fits.
+-template <typename T, typename U>
+-struct CheckedLshOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = T;
+- template <typename V>
+- static constexpr bool Do(T x, U shift, V* result) {
+- // Disallow negative numbers and verify the shift is in bounds.
+- if (BASE_NUMERICS_LIKELY(!IsValueNegative(x) &&
+- as_unsigned(shift) <
+- as_unsigned(std::numeric_limits<T>::digits))) {
+- // Shift as unsigned to avoid undefined behavior.
+- *result = static_cast<V>(as_unsigned(x) << shift);
+- // If the shift can be reversed, we know it was valid.
+- return *result >> shift == x;
+- }
+-
+- // Handle the legal corner-case of a full-width signed shift of zero.
+- return std::is_signed<T>::value && !x &&
+- as_unsigned(shift) == as_unsigned(std::numeric_limits<T>::digits);
+- }
+-};
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedRshOp {};
+-
+-// Right shift. Shifts less than 0 or greater than or equal to the number
+-// of bits in the promoted type are undefined. Otherwise, it is always defined,
+-// but a right shift of a negative value is implementation-dependent.
+-template <typename T, typename U>
+-struct CheckedRshOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = T;
+- template <typename V>
+- static bool Do(T x, U shift, V* result) {
+- // Use the type conversion push negative values out of range.
+- if (BASE_NUMERICS_LIKELY(as_unsigned(shift) <
+- IntegerBitsPlusSign<T>::value)) {
+- T tmp = x >> shift;
+- *result = static_cast<V>(tmp);
+- return IsValueInRangeForNumericType<V>(tmp);
+- }
+- return false;
+- }
+-};
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedAndOp {};
+-
+-// For simplicity we support only unsigned integer results.
+-template <typename T, typename U>
+-struct CheckedAndOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename std::make_unsigned<
+- typename MaxExponentPromotion<T, U>::type>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- result_type tmp = static_cast<result_type>(x) & static_cast<result_type>(y);
+- *result = static_cast<V>(tmp);
+- return IsValueInRangeForNumericType<V>(tmp);
+- }
+-};
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedOrOp {};
+-
+-// For simplicity we support only unsigned integers.
+-template <typename T, typename U>
+-struct CheckedOrOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename std::make_unsigned<
+- typename MaxExponentPromotion<T, U>::type>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- result_type tmp = static_cast<result_type>(x) | static_cast<result_type>(y);
+- *result = static_cast<V>(tmp);
+- return IsValueInRangeForNumericType<V>(tmp);
+- }
+-};
+-
+-template <typename T, typename U, class Enable = void>
+-struct CheckedXorOp {};
+-
+-// For simplicity we support only unsigned integers.
+-template <typename T, typename U>
+-struct CheckedXorOp<T,
+- U,
+- typename std::enable_if<std::is_integral<T>::value &&
+- std::is_integral<U>::value>::type> {
+- using result_type = typename std::make_unsigned<
+- typename MaxExponentPromotion<T, U>::type>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- result_type tmp = static_cast<result_type>(x) ^ static_cast<result_type>(y);
+- *result = static_cast<V>(tmp);
+- return IsValueInRangeForNumericType<V>(tmp);
+- }
+-};
+-
+-// Max doesn't really need to be implemented this way because it can't fail,
+-// but it makes the code much cleaner to use the MathOp wrappers.
+-template <typename T, typename U, class Enable = void>
+-struct CheckedMaxOp {};
+-
+-template <typename T, typename U>
+-struct CheckedMaxOp<
+- T,
+- U,
+- typename std::enable_if<std::is_arithmetic<T>::value &&
+- std::is_arithmetic<U>::value>::type> {
+- using result_type = typename MaxExponentPromotion<T, U>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- result_type tmp = IsGreater<T, U>::Test(x, y) ? static_cast<result_type>(x)
+- : static_cast<result_type>(y);
+- *result = static_cast<V>(tmp);
+- return IsValueInRangeForNumericType<V>(tmp);
+- }
+-};
+-
+-// Min doesn't really need to be implemented this way because it can't fail,
+-// but it makes the code much cleaner to use the MathOp wrappers.
+-template <typename T, typename U, class Enable = void>
+-struct CheckedMinOp {};
+-
+-template <typename T, typename U>
+-struct CheckedMinOp<
+- T,
+- U,
+- typename std::enable_if<std::is_arithmetic<T>::value &&
+- std::is_arithmetic<U>::value>::type> {
+- using result_type = typename LowestValuePromotion<T, U>::type;
+- template <typename V>
+- static constexpr bool Do(T x, U y, V* result) {
+- result_type tmp = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x)
+- : static_cast<result_type>(y);
+- *result = static_cast<V>(tmp);
+- return IsValueInRangeForNumericType<V>(tmp);
+- }
+-};
+-
+-// This is just boilerplate that wraps the standard floating point arithmetic.
+-// A macro isn't the nicest solution, but it beats rewriting these repeatedly.
+-#define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \
+- template <typename T, typename U> \
+- struct Checked##NAME##Op< \
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list