/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; namespace { /// Covers filter/source/pdf/ fixes. class Test : public UnoApiTest { public: Test() : UnoApiTest(u"/filter/qa/data/"_ustr) { } void setUp() override; void doTestCommentsInMargin(bool commentsInMarginEnabled); }; void Test::setUp() { UnoApiTest::setUp(); MacrosTest::setUpX509(m_directories, u"filter_pdf"_ustr); } CPPUNIT_TEST_FIXTURE(Test, testSignCertificateSubjectName) { std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; uno::Reference xSEInitializer = xml::crypto::SEInitializer::create(m_xContext); uno::Reference xSecurityContext = xSEInitializer->createSecurityContext(OUString()); uno::Reference xSecurityEnvironment = xSecurityContext->getSecurityEnvironment(); uno::Sequence aFilterData{ comphelper::makePropertyValue(u"SignPDF"_ustr, true), comphelper::makePropertyValue( u"SignCertificateSubjectName"_ustr, u"CN=Xmlsecurity RSA Test example Alice,O=Xmlsecurity RSA Test,ST=England,C=UK"_ustr), }; if (!GetValidCertificate(xSecurityEnvironment->getPersonalCertificates(), xSecurityEnvironment, aFilterData)) { return; } // Given an empty document: loadFromURL(u"private:factory/swriter"_ustr); // When exporting to PDF, and referring to a certificate using a subject name: uno::Reference xFactory = getMultiServiceFactory(); uno::Reference xFilter( xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), uno::UNO_QUERY); uno::Reference xExporter(xFilter, uno::UNO_QUERY); xExporter->setSourceDocument(mxComponent); SvMemoryStream aStream; uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); uno::Sequence aDescriptor{ comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_pdf_Export"_ustr), comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData), comphelper::makePropertyValue(u"OutputStream"_ustr, xOutputStream), }; xFilter->filter(aDescriptor); // Then make sure the resulting PDF has a signature: std::unique_ptr pPdfDocument = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); // Without the accompanying fix in place, this test would have failed, as signing was enabled // without configuring a certificate, so the whole export failed. CPPUNIT_ASSERT(pPdfDocument); CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getSignatureCount()); } CPPUNIT_TEST_FIXTURE(Test, testPdfDecompositionSize) { std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; // Given an empty Writer document: loadFromURL(u"private:factory/swriter"_ustr); // When inserting a 267 points wide PDF image into the document: uno::Sequence aArgs = { comphelper::makePropertyValue(u"FileName"_ustr, createFileURL(u"picture.pdf")), }; dispatchCommand(mxComponent, u".uno:InsertGraphic"_ustr, aArgs); // Then make sure that its size is correct: uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xDrawPage = xDrawPageSupplier->getDrawPage(); uno::Reference xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); auto xGraphic = xShape->getPropertyValue(u"Graphic"_ustr).get>(); CPPUNIT_ASSERT(xGraphic.is()); Graphic aGraphic(xGraphic); basegfx::B2DRange aRange = aGraphic.getVectorGraphicData()->getRange(); // Without the accompanying fix in place, this test would have failed with: // - Expected: 9437 // - Actual : 34176 // i.e. the width was too large, it used all width of the body frame. // 9437 mm100 is 267.507 points from the file. #if defined MACOSX // TODO the bitmap size is larger (75486) on macOS, but that should not affect the logic size. (void)aRange; #else // Unfortunately, this test is DPI-dependent. // Use some allowance (~1/2 pt) to let it pass on non-default DPI. CPPUNIT_ASSERT_DOUBLES_EQUAL(9437, aRange.getWidth(), 20.0); #endif } void Test::doTestCommentsInMargin(bool commentsInMarginEnabled) { std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; loadFromFile(u"commentsInMargin.odt"); uno::Reference xFactory = getMultiServiceFactory(); uno::Reference xFilter( xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), uno::UNO_QUERY); uno::Reference xExporter(xFilter, uno::UNO_QUERY); xExporter->setSourceDocument(mxComponent); SvMemoryStream aStream; uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); uno::Sequence aFilterData{ comphelper::makePropertyValue( u"ExportNotesInMargin"_ustr, commentsInMarginEnabled) }; uno::Sequence aDescriptor{ comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_pdf_Export"_ustr), comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData), comphelper::makePropertyValue(u"OutputStream"_ustr, xOutputStream), }; xFilter->filter(aDescriptor); // Make sure the number of objects is correct std::unique_ptr pPdfDocument = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); CPPUNIT_ASSERT(pPdfDocument); if (commentsInMarginEnabled) { // Unfortunately, the comment box is DPI dependent, and the lines there may split // at higher DPIs, creating additional objects on import, hence the "_GREATER" auto pPage = pPdfDocument->openPage(0); int nObjs = pPage->getObjectCount(); CPPUNIT_ASSERT_GREATER(8, nObjs); bool bFound = false; int i = 0; for (; i < nObjs && !bFound; ++i) { auto pObj = pPage->getObject(i); if (pObj->getText(pPage->getTextPage()) == "Nice comment over here.") bFound = true; } CPPUNIT_ASSERT_MESSAGE("The comment text was not found.", bFound); CPPUNIT_ASSERT_GREATER(400., pPage->getObject(i - 1)->getBounds().getMinX()); } else CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->openPage(0)->getObjectCount()); } CPPUNIT_TEST_FIXTURE(Test, testCommentsInMarginEnabled) { doTestCommentsInMargin(true); } CPPUNIT_TEST_FIXTURE(Test, testCommentsInMarginDisabled) { doTestCommentsInMargin(false); } CPPUNIT_TEST_FIXTURE(Test, testCommentsInMarginEnabledTiledAnnotationsEnabled) { comphelper::LibreOfficeKit::setActive(true); comphelper::LibreOfficeKit::setTiledAnnotations(true); doTestCommentsInMargin(true); } CPPUNIT_TEST_FIXTURE(Test, testCommentsInMarginDisabledTiledAnnotationsEnabled) { comphelper::LibreOfficeKit::setActive(true); comphelper::LibreOfficeKit::setTiledAnnotations(true); doTestCommentsInMargin(false); } CPPUNIT_TEST_FIXTURE(Test, testCommentsInMarginEnabledTiledAnnotationsDisabled) { comphelper::LibreOfficeKit::setActive(true); comphelper::LibreOfficeKit::setTiledAnnotations(false); doTestCommentsInMargin(true); } CPPUNIT_TEST_FIXTURE(Test, testCommentsInMarginDisabledTiledAnnotationsDisabled) { comphelper::LibreOfficeKit::setActive(true); comphelper::LibreOfficeKit::setTiledAnnotations(false); doTestCommentsInMargin(false); } CPPUNIT_TEST_FIXTURE(Test, testWatermarkColor) { // Given an empty Writer document: std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; loadFromURL(u"private:factory/swriter"_ustr); // When exporting that as PDF with a red watermark: uno::Reference xFactory = getMultiServiceFactory(); uno::Reference xFilter( xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), uno::UNO_QUERY); uno::Reference xExporter(xFilter, uno::UNO_QUERY); xExporter->setSourceDocument(mxComponent); SvMemoryStream aStream; uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); uno::Sequence aFilterData{ comphelper::makePropertyValue(u"Watermark"_ustr, u"X"_ustr), comphelper::makePropertyValue(u"WatermarkColor"_ustr, static_cast(0xff0000)), }; uno::Sequence aDescriptor{ comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_pdf_Export"_ustr), comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData), comphelper::makePropertyValue(u"OutputStream"_ustr, xOutputStream), }; xFilter->filter(aDescriptor); // Then make sure that the watermark color is correct: std::unique_ptr pPdfDocument = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); CPPUNIT_ASSERT(pPdfDocument); std::unique_ptr pPage = pPdfDocument->openPage(0); CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); std::unique_ptr pPageObject = pPage->getObject(0); CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); std::unique_ptr pFormObject = pPageObject->getFormObject(0); Color aFillColor = pFormObject->getFillColor(); // Without the accompanying fix in place, this test would have failed with: // - Expected: rgba[ff0000ff] // - Actual : rgba[00ff00ff] // i.e. the color was the (default) green, not red. CPPUNIT_ASSERT_EQUAL(Color(ColorTransparency, 0xff0000), aFillColor); } CPPUNIT_TEST_FIXTURE(Test, testWatermarkFontHeight) { // Given an empty Writer document: std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; loadFromURL(u"private:factory/swriter"_ustr); // When exporting that as PDF with a 100pt-sized watermark: uno::Reference xFactory = getMultiServiceFactory(); uno::Reference xFilter( xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), uno::UNO_QUERY); uno::Reference xExporter(xFilter, uno::UNO_QUERY); xExporter->setSourceDocument(mxComponent); SvMemoryStream aStream; uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); sal_Int32 nExpectedFontSize = 100; uno::Sequence aFilterData{ comphelper::makePropertyValue(u"Watermark"_ustr, u"X"_ustr), comphelper::makePropertyValue(u"WatermarkFontHeight"_ustr, nExpectedFontSize), }; uno::Sequence aDescriptor{ comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_pdf_Export"_ustr), comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData), comphelper::makePropertyValue(u"OutputStream"_ustr, xOutputStream), }; xFilter->filter(aDescriptor); // Then make sure that the watermark font size is correct: std::unique_ptr pPdfDocument = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); CPPUNIT_ASSERT(pPdfDocument); std::unique_ptr pPage = pPdfDocument->openPage(0); CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); std::unique_ptr pPageObject = pPage->getObject(0); CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); std::unique_ptr pFormObject = pPageObject->getFormObject(0); sal_Int32 nFontSize = pFormObject->getFontSize(); // Without the accompanying fix in place, this test would have failed with: // - Expected: 100 // - Actual : 594 // i.e. the font size was automatic, could not specify an explicit size. CPPUNIT_ASSERT_EQUAL(nExpectedFontSize, nFontSize); } CPPUNIT_TEST_FIXTURE(Test, testWatermarkFontName) { // Given an empty Writer document: std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; loadFromURL(u"private:factory/swriter"_ustr); // When exporting that as PDF with a serif watermark: uno::Reference xFactory = getMultiServiceFactory(); uno::Reference xFilter( xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), uno::UNO_QUERY); uno::Reference xExporter(xFilter, uno::UNO_QUERY); xExporter->setSourceDocument(mxComponent); SvMemoryStream aStream; uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); OUString aExpectedFontName(u"Liberation Serif"_ustr); uno::Sequence aFilterData{ comphelper::makePropertyValue(u"Watermark"_ustr, u"X"_ustr), comphelper::makePropertyValue(u"WatermarkFontName"_ustr, aExpectedFontName), }; uno::Sequence aDescriptor{ comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_pdf_Export"_ustr), comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData), comphelper::makePropertyValue(u"OutputStream"_ustr, xOutputStream), }; xFilter->filter(aDescriptor); // Then make sure that the watermark font name is correct: std::unique_ptr pPdfDocument = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); CPPUNIT_ASSERT(pPdfDocument); std::unique_ptr pPage = pPdfDocument->openPage(0); CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); std::unique_ptr pPageObject = pPage->getObject(0); CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); std::unique_ptr pFormObject = pPageObject->getFormObject(0); OUString aFontName = pFormObject->getFontName(); // Without the accompanying fix in place, this test would have failed with: // - Expected: Liberation Serif // - Actual : Helvetica // i.e. the font name was sans, could not specify an explicit name. CPPUNIT_ASSERT_EQUAL(aExpectedFontName, aFontName); } CPPUNIT_TEST_FIXTURE(Test, testWatermarkRotateAngle) { // Given an empty Writer document: std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; loadFromURL(u"private:factory/swriter"_ustr); // When exporting that as PDF with a rotated watermark: uno::Reference xFactory = getMultiServiceFactory(); uno::Reference xFilter( xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), uno::UNO_QUERY); uno::Reference xExporter(xFilter, uno::UNO_QUERY); xExporter->setSourceDocument(mxComponent); SvMemoryStream aStream; uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); // 45.0 degrees, counter-clockwise. sal_Int32 nExpectedRotateAngle = 45; uno::Sequence aFilterData{ comphelper::makePropertyValue(u"Watermark"_ustr, u"X"_ustr), comphelper::makePropertyValue(u"WatermarkRotateAngle"_ustr, nExpectedRotateAngle * 10), }; uno::Sequence aDescriptor{ comphelper::makePropertyValue(u"FilterName"_ustr, u"writer_pdf_Export"_ustr), comphelper::makePropertyValue(u"FilterData"_ustr, aFilterData), comphelper::makePropertyValue(u"OutputStream"_ustr, xOutputStream), }; xFilter->filter(aDescriptor); // Then make sure that the watermark rotation angle is correct: std::unique_ptr pPdfDocument = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); CPPUNIT_ASSERT(pPdfDocument); std::unique_ptr pPage = pPdfDocument->openPage(0); CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); std::unique_ptr pPageObject = pPage->getObject(0); CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); std::unique_ptr pFormObject = pPageObject->getFormObject(0); basegfx::B2DHomMatrix aMatrix = pFormObject->getMatrix(); basegfx::B2DTuple aScale; basegfx::B2DTuple aTranslate; double fRotate{}; double fShearX{}; aMatrix.decompose(aScale, aTranslate, fRotate, fShearX); sal_Int32 nActualRotateAngle = NormAngle360(basegfx::rad2deg<1>(fRotate)); // Without the accompanying fix in place, this test would have failed with: // - Expected: 45 // - Actual : 270 // i.e. the rotation angle was 270 for an A4 page, not the requested 45 degrees. CPPUNIT_ASSERT_EQUAL(nExpectedRotateAngle, nActualRotateAngle); } #ifdef UNX CPPUNIT_TEST_FIXTURE(Test, testSignCertificatePEM) { // Given an empty document: std::shared_ptr pPDFium = vcl::pdf::PDFiumLibrary::get(); if (!pPDFium) return; uno::Reference xSEInitializer = xml::crypto::SEInitializer::create(m_xContext); uno::Reference xSecurityContext = xSEInitializer->createSecurityContext(OUString()); uno::Reference xSecurityEnvironment = xSecurityContext->getSecurityEnvironment(); OUString aKeyPath = createFileURL(u"key.pem"); SvFileStream aKeyStream(aKeyPath, StreamMode::READ); OUString aKeyPem = OUString::fromUtf8(read_uInt8s_ToOString(aKeyStream, aKeyStream.remainingSize())); OUString aCertPath = createFileURL(u"cert.pem"); SvFileStream aCertStream(aCertPath, StreamMode::READ); OUString aCertPem = OUString::fromUtf8(read_uInt8s_ToOString(aCertStream, aCertStream.remainingSize())); OUString aCaPath = createFileURL(u"ca.pem"); SvFileStream aCaStream(aCaPath, StreamMode::READ); OUString aCaPem = OUString::fromUtf8(read_uInt8s_ToOString(aCaStream, aCaStream.remainingSize())); uno::Sequence aFilterData{ comphelper::makePropertyValue("SignPDF", true), comphelper::makePropertyValue("SignCertificateCertPem", aCertPem), comphelper::makePropertyValue("SignCertificateKeyPem", aKeyPem), comphelper::makePropertyValue("aSignCertificateCaPem", aCaPem), }; loadFromURL(u"private:factory/swriter"_ustr); // When exporting to PDF, and referring to a certificate using a cert/key/ca PEM, which is not // in the NSS database: uno::Reference xFactory = getMultiServiceFactory(); uno::Reference xFilter( xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); uno::Reference xExporter(xFilter, uno::UNO_QUERY); xExporter->setSourceDocument(mxComponent); SvMemoryStream aStream; uno::Reference xOutputStream(new utl::OStreamWrapper(aStream)); uno::Sequence aDescriptor{ comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), comphelper::makePropertyValue("FilterData", aFilterData), comphelper::makePropertyValue("OutputStream", xOutputStream), }; xFilter->filter(aDescriptor); // Then make sure the resulting PDF has a signature: std::unique_ptr pPdfDocument = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); // Without the accompanying fix in place, this test would have failed, as signing was enabled // without configuring a certificate, so the whole export failed. CPPUNIT_ASSERT(pPdfDocument); CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getSignatureCount()); } #endif } CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */