RepositoryExternal.mk | 1 vcl/source/filter/ipdf/pdfdocument.cxx | 55 ++++++++- xmlsecurity/CppunitTest_xmlsecurity_signing.mk | 3 xmlsecurity/qa/unit/signing/data/add-visible-signature.pdf |binary xmlsecurity/qa/unit/signing/signing.cxx | 76 +++++++++++++ 5 files changed, 131 insertions(+), 4 deletions(-)
New commits: commit 5b7b7731a2d1941d39cd10c0b12dab427d2cea07 Author: Miklos Vajna <[email protected]> AuthorDate: Fri Jun 19 17:21:58 2020 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Jun 29 09:10:30 2020 +0200 sd signature line: add testcase Fails with commit 9b7a890fd59744459692d7f66402c6bdd25acec4 (sd signature line: include shape in the appearance widget, 2020-06-19) reverted. (cherry picked from commit fb8f0ef26d57986bd27c6c5088939c32453e6b06) Change-Id: Ib237774374553af5d37c9deaffdea6fae65a28f4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97252 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/RepositoryExternal.mk b/RepositoryExternal.mk index f6003fc22aba..92dca6d4d04e 100644 --- a/RepositoryExternal.mk +++ b/RepositoryExternal.mk @@ -4135,6 +4135,7 @@ endef ifneq ($(ENABLE_PDFIUM),) define gb_LinkTarget__use_pdfium $(call gb_LinkTarget_set_include,$(1),\ + -I$(call gb_UnpackedTarball_get_dir,pdfium) \ -I$(call gb_UnpackedTarball_get_dir,pdfium)/public \ $$(INCLUDE) \ ) diff --git a/xmlsecurity/CppunitTest_xmlsecurity_signing.mk b/xmlsecurity/CppunitTest_xmlsecurity_signing.mk index 84e7a76c043b..3554b3eaa468 100644 --- a/xmlsecurity/CppunitTest_xmlsecurity_signing.mk +++ b/xmlsecurity/CppunitTest_xmlsecurity_signing.mk @@ -22,16 +22,19 @@ $(eval $(call gb_CppunitTest_use_libraries,xmlsecurity_signing, \ sal \ sax \ sfx \ + svx \ test \ tl \ unotest \ utl \ + vcl \ xmlsecurity \ )) $(eval $(call gb_CppunitTest_use_externals,xmlsecurity_signing,\ boost_headers \ libxml2 \ + $(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \ )) $(eval $(call gb_CppunitTest_set_include,xmlsecurity_signing,\ diff --git a/xmlsecurity/qa/unit/signing/data/add-visible-signature.pdf b/xmlsecurity/qa/unit/signing/data/add-visible-signature.pdf new file mode 100644 index 000000000000..8dedb6998b23 Binary files /dev/null and b/xmlsecurity/qa/unit/signing/data/add-visible-signature.pdf differ diff --git a/xmlsecurity/qa/unit/signing/signing.cxx b/xmlsecurity/qa/unit/signing/signing.cxx index f2039b609e7e..04eb91e8822b 100644 --- a/xmlsecurity/qa/unit/signing/signing.cxx +++ b/xmlsecurity/qa/unit/signing/signing.cxx @@ -27,6 +27,8 @@ #include <com/sun/star/security/DocumentDigitalSignatures.hpp> #include <com/sun/star/security/XDocumentDigitalSignatures.hpp> #include <com/sun/star/xml/crypto/SEInitializer.hpp> +#include <com/sun/star/drawing/XDrawPagesSupplier.hpp> +#include <com/sun/star/view/XSelectionSupplier.hpp> #include <comphelper/processfactory.hxx> #include <comphelper/propertysequence.hxx> @@ -51,6 +53,16 @@ #include <sfx2/docfilt.hxx> #include <officecfg/Office/Common.hxx> #include <comphelper/configuration.hxx> +#include <svx/signaturelinehelper.hxx> +#include <sfx2/viewsh.hxx> +#include <comphelper/propertyvalue.hxx> +#include <vcl/filter/PDFiumLibrary.hxx> + +#if HAVE_FEATURE_PDFIUM +#include <fpdf_annot.h> +#include <fpdfview.h> +#include <cpp/fpdf_scopers.h> +#endif using namespace com::sun::star; @@ -629,6 +641,70 @@ CPPUNIT_TEST_FIXTURE(SigningTest, testPDFNo) static_cast<int>(pObjectShell->GetDocumentSignatureState())); } +#if HAVE_FEATURE_PDFIUM +CPPUNIT_TEST_FIXTURE(SigningTest, testPDFAddVisibleSignature) +{ + // Given: copy the test document to a temporary file, as it'll be modified. + utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + OUString aSourceURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "add-visible-signature.pdf"; + OUString aURL = aTempFile.GetURL(); + osl::File::RC eRet = osl::File::copy(aSourceURL, aURL); + CPPUNIT_ASSERT_EQUAL(osl::File::RC::E_None, eRet); + + // Open it. + uno::Sequence<beans::PropertyValue> aArgs = { comphelper::makePropertyValue("ReadOnly", true) }; + mxComponent = loadFromDesktop(aURL, "com.sun.star.drawing.DrawingDocument", aArgs); + SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(mxComponent.get()); + CPPUNIT_ASSERT(pBaseModel); + SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); + CPPUNIT_ASSERT(pObjectShell); + + // Add a signature line. + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XShape> xShape( + xFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), uno::UNO_QUERY); + xShape->setPosition(awt::Point(1000, 15000)); + xShape->setSize(awt::Size(10000, 10000)); + uno::Reference<drawing::XDrawPagesSupplier> xSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<drawing::XDrawPages> xDrawPages = xSupplier->getDrawPages(); + uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPages->getByIndex(0), uno::UNO_QUERY); + xDrawPage->add(xShape); + + // Select it and assign a certificate. + uno::Reference<view::XSelectionSupplier> xSelectionSupplier(pBaseModel->getCurrentController(), + uno::UNO_QUERY); + xSelectionSupplier->select(uno::makeAny(xShape)); + uno::Sequence<uno::Reference<security::XCertificate>> aCertificates + = mxSecurityContext->getSecurityEnvironment()->getPersonalCertificates(); + if (!aCertificates.hasElements()) + { + return; + } + SdrView* pView = SfxViewShell::Current()->GetDrawView(); + svx::SignatureLineHelper::setShapeCertificate(pView, aCertificates[0]); + + // When: do the actual signing. + pObjectShell->SignDocumentContentUsingCertificate(aCertificates[0]); + + // Then: count the # of shapes on the signature widget/annotation. + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + SvFileStream aFile(aTempFile.GetURL(), StreamMode::READ); + SvMemoryStream aMemory; + aMemory.WriteStream(aFile); + ScopedFPDFDocument pPdfDocument( + FPDF_LoadMemDocument(aMemory.GetData(), aMemory.GetSize(), /*password=*/nullptr)); + ScopedFPDFPage pPdfPage(FPDF_LoadPage(pPdfDocument.get(), /*page_index=*/0)); + CPPUNIT_ASSERT_EQUAL(1, FPDFPage_GetAnnotCount(pPdfPage.get())); + ScopedFPDFAnnotation pAnnot(FPDFPage_GetAnnot(pPdfPage.get(), 0)); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 4 + // - Actual : 0 + // i.e. the signature was there, but it was empty / not visible. + CPPUNIT_ASSERT_EQUAL(4, FPDFAnnot_GetObjectCount(pAnnot.get())); +} +#endif + #endif CPPUNIT_TEST_FIXTURE(SigningTest, test96097Calc) commit e9ee1289b2307af3d0a6023f5c0fa01c92f3b26f Author: Miklos Vajna <[email protected]> AuthorDate: Fri Jun 19 14:08:44 2020 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Mon Jun 29 09:10:16 2020 +0200 sd signature line: include shape in the appearance widget With this, finally the following works: 1) file -> digital signatures -> sign existing pdf 2) file -> digital signatures -> signature line 3) draw a rectangle 4) popup appears, select signing certificate 5) click on the "finish signing" button on the infobar The resulting pdf will have a signature on it, together with the pdf export of the signature line shape. (cherry picked from commit 9b7a890fd59744459692d7f66402c6bdd25acec4) Change-Id: Icef701aaa6fd4a625acb37094ad34b88283caf42 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/97251 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx index 85a5d19ca52f..401ea9ff1844 100644 --- a/vcl/source/filter/ipdf/pdfdocument.cxx +++ b/vcl/source/filter/ipdf/pdfdocument.cxx @@ -28,6 +28,8 @@ #include <vcl/pdfwriter.hxx> #include <o3tl/safeint.hxx> +#include <pdf/objectcopier.hxx> + using namespace com::sun::star; namespace vcl @@ -265,6 +267,10 @@ sal_Int32 PDFDocument::WriteSignatureObject(const OUString& rDescription, bool b sal_Int32 PDFDocument::WriteAppearanceObject(tools::Rectangle& rSignatureRectangle) { + PDFDocument aPDFDocument; + filter::PDFObjectElement* pPage = nullptr; + std::vector<filter::PDFObjectElement*> aContentStreams; + if (!m_aSignatureLine.empty()) { // Parse the PDF data of signature line: we can set the signature rectangle to non-empty @@ -272,7 +278,6 @@ sal_Int32 PDFDocument::WriteAppearanceObject(tools::Rectangle& rSignatureRectang SvMemoryStream aPDFStream; aPDFStream.WriteBytes(m_aSignatureLine.data(), m_aSignatureLine.size()); aPDFStream.Seek(0); - filter::PDFDocument aPDFDocument; if (!aPDFDocument.Read(aPDFStream)) { SAL_WARN("vcl.filter", @@ -287,7 +292,7 @@ sal_Int32 PDFDocument::WriteAppearanceObject(tools::Rectangle& rSignatureRectang return -1; } - filter::PDFObjectElement* pPage = aPages[0]; + pPage = aPages[0]; if (!pPage) { SAL_WARN("vcl.filter", "PDFDocument::WriteAppearanceObject: no page"); @@ -318,6 +323,17 @@ sal_Int32 PDFDocument::WriteAppearanceObject(tools::Rectangle& rSignatureRectang return -1; } rSignatureRectangle.setHeight(pHeight->GetValue()); + + if (PDFObjectElement* pContentStream = pPage->LookupObject("Contents")) + { + aContentStreams.push_back(pContentStream); + } + + if (aContentStreams.empty()) + { + SAL_WARN("vcl.filter", "PDFDocument::WriteAppearanceObject: no content stream"); + return -1; + } } m_aSignatureLine.clear(); @@ -330,14 +346,45 @@ sal_Int32 PDFDocument::WriteAppearanceObject(tools::Rectangle& rSignatureRectang aEditBuffer.WriteUInt32AsString(nAppearanceId); aEditBuffer.WriteCharPtr(" 0 obj\n"); aEditBuffer.WriteCharPtr("<</Type/XObject\n/Subtype/Form\n"); + + PDFObjectCopier aCopier(*this); + if (!aContentStreams.empty()) + { + OStringBuffer aBuffer; + aCopier.copyPageResources(pPage, aBuffer); + aEditBuffer.WriteOString(aBuffer.makeStringAndClear()); + } + aEditBuffer.WriteCharPtr("/BBox[0 0 "); aEditBuffer.WriteOString(OString::number(rSignatureRectangle.getWidth())); aEditBuffer.WriteCharPtr(" "); aEditBuffer.WriteOString(OString::number(rSignatureRectangle.getHeight())); - aEditBuffer.WriteCharPtr("]\n/Length 0\n>>\n"); - aEditBuffer.WriteCharPtr("stream\n\nendstream\nendobj\n\n"); + aEditBuffer.WriteCharPtr("]\n/Length "); // Add the object to the doc-level edit buffer and update the offset. + SvMemoryStream aStream; + bool bCompressed = false; + sal_Int32 nLength = 0; + if (!aContentStreams.empty()) + { + nLength = PDFObjectCopier::copyPageStreams(aContentStreams, aStream, bCompressed); + } + aEditBuffer.WriteOString(OString::number(nLength)); + if (bCompressed) + { + aEditBuffer.WriteOString(" /Filter/FlateDecode"); + } + + aEditBuffer.WriteCharPtr("\n>>\n"); + + aEditBuffer.WriteCharPtr("stream\n"); + + // Copy the original page streams to the form XObject stream. + aStream.Seek(0); + aEditBuffer.WriteStream(aStream); + + aEditBuffer.WriteCharPtr("\nendstream\nendobj\n\n"); + aEditBuffer.Seek(0); XRefEntry aAppearanceEntry; aAppearanceEntry.SetOffset(m_aEditBuffer.Tell()); _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
