diff options
Diffstat (limited to 'filter/qa/pdf.cxx')
-rw-r--r-- | filter/qa/pdf.cxx | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/filter/qa/pdf.cxx b/filter/qa/pdf.cxx new file mode 100644 index 000000000000..9ab11cd36d71 --- /dev/null +++ b/filter/qa/pdf.cxx @@ -0,0 +1,373 @@ +/* -*- 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/unoapi_test.hxx> + +#include <com/sun/star/document/XExporter.hpp> +#include <com/sun/star/document/XFilter.hpp> +#include <com/sun/star/xml/crypto/SEInitializer.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <tools/stream.hxx> +#include <unotools/streamwrap.hxx> +#include <vcl/filter/PDFiumLibrary.hxx> +#include <tools/helpers.hxx> + +using namespace ::com::sun::star; + +namespace +{ +/// Covers filter/source/pdf/ fixes. +class Test : public UnoApiTest +{ +public: + Test() + : UnoApiTest("/filter/qa/data/") + { + } + + void setUp() override; + void doTestCommentsInMargin(bool commentsInMarginEnabled); +}; + +void Test::setUp() +{ + UnoApiTest::setUp(); + + MacrosTest::setUpX509(m_directories, "filter_pdf"); +} + +CPPUNIT_TEST_FIXTURE(Test, testSignCertificateSubjectName) +{ + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + + uno::Reference<xml::crypto::XSEInitializer> xSEInitializer + = xml::crypto::SEInitializer::create(mxComponentContext); + uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext + = xSEInitializer->createSecurityContext(OUString()); + uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment + = xSecurityContext->getSecurityEnvironment(); + uno::Sequence<beans::PropertyValue> aFilterData{ + comphelper::makePropertyValue("SignPDF", true), + comphelper::makePropertyValue( + "SignCertificateSubjectName", + OUString( + "CN=Xmlsecurity RSA Test example Alice,O=Xmlsecurity RSA Test,ST=England,C=UK")), + }; + if (!GetValidCertificate(xSecurityEnvironment->getPersonalCertificates(), xSecurityEnvironment, + aFilterData)) + { + return; + } + + // Given an empty document: + mxComponent.set(loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When exporting to PDF, and referring to a certificate using a subject name: + uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory(); + uno::Reference<document::XFilter> xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(mxComponent); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + + uno::Sequence<beans::PropertyValue> 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<vcl::pdf::PDFiumDocument> 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<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + + // Given an empty Writer document: + mxComponent.set(loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When inserting a 267 points wide PDF image into the document: + uno::Sequence<beans::PropertyValue> aArgs = { + comphelper::makePropertyValue("FileName", createFileURL(u"picture.pdf")), + }; + dispatchCommand(mxComponent, ".uno:InsertGraphic", aArgs); + + // Then make sure that its size is correct: + uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XIndexAccess> xDrawPage = xDrawPageSupplier->getDrawPage(); + uno::Reference<beans::XPropertySet> xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); + auto xGraphic = xShape->getPropertyValue("Graphic").get<uno::Reference<graphic::XGraphic>>(); + 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<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + + loadFromFile(u"commentsInMargin.odt"); + uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory(); + uno::Reference<document::XFilter> xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(mxComponent); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + uno::Sequence<beans::PropertyValue> aFilterData{ comphelper::makePropertyValue( + "ExportNotesInMargin", commentsInMarginEnabled) }; + uno::Sequence<beans::PropertyValue> aDescriptor{ + comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xOutputStream), + }; + xFilter->filter(aDescriptor); + + // Make sure the number of objects is correct + std::unique_ptr<vcl::pdf::PDFiumDocument> 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" + CPPUNIT_ASSERT_GREATER(8, pPdfDocument->openPage(0)->getObjectCount()); + } + else + CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->openPage(0)->getObjectCount()); +} + +CPPUNIT_TEST_FIXTURE(Test, testCommentsInMargin) +{ + // Test that setting/unsetting the "ExportNotesInMargin" property works correctly + doTestCommentsInMargin(true); + doTestCommentsInMargin(false); +} + +CPPUNIT_TEST_FIXTURE(Test, testWatermarkColor) +{ + // Given an empty Writer document: + std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + mxComponent.set(loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When exporting that as PDF with a red watermark: + uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory(); + uno::Reference<document::XFilter> xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(mxComponent); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + uno::Sequence<beans::PropertyValue> aFilterData{ + comphelper::makePropertyValue("Watermark", OUString("X")), + comphelper::makePropertyValue("WatermarkColor", static_cast<sal_Int32>(0xff0000)), + }; + uno::Sequence<beans::PropertyValue> aDescriptor{ + comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xOutputStream), + }; + xFilter->filter(aDescriptor); + + // Then make sure that the watermark color is correct: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); + CPPUNIT_ASSERT(pPdfDocument); + std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0); + CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> 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<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + mxComponent.set(loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When exporting that as PDF with a 100pt-sized watermark: + uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory(); + uno::Reference<document::XFilter> xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(mxComponent); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + sal_Int32 nExpectedFontSize = 100; + uno::Sequence<beans::PropertyValue> aFilterData{ + comphelper::makePropertyValue("Watermark", OUString("X")), + comphelper::makePropertyValue("WatermarkFontHeight", nExpectedFontSize), + }; + uno::Sequence<beans::PropertyValue> aDescriptor{ + comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xOutputStream), + }; + xFilter->filter(aDescriptor); + + // Then make sure that the watermark font size is correct: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); + CPPUNIT_ASSERT(pPdfDocument); + std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0); + CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> 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<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + mxComponent.set(loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When exporting that as PDF with a serif watermark: + uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory(); + uno::Reference<document::XFilter> xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(mxComponent); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + OUString aExpectedFontName("Liberation Serif"); + uno::Sequence<beans::PropertyValue> aFilterData{ + comphelper::makePropertyValue("Watermark", OUString("X")), + comphelper::makePropertyValue("WatermarkFontName", aExpectedFontName), + }; + uno::Sequence<beans::PropertyValue> aDescriptor{ + comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xOutputStream), + }; + xFilter->filter(aDescriptor); + + // Then make sure that the watermark font name is correct: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); + CPPUNIT_ASSERT(pPdfDocument); + std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0); + CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> 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<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get(); + if (!pPDFium) + return; + mxComponent.set(loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument")); + + // When exporting that as PDF with a rotated watermark: + uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory(); + uno::Reference<document::XFilter> xFilter( + xFactory->createInstance("com.sun.star.document.PDFFilter"), uno::UNO_QUERY); + uno::Reference<document::XExporter> xExporter(xFilter, uno::UNO_QUERY); + xExporter->setSourceDocument(mxComponent); + SvMemoryStream aStream; + uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aStream)); + // 45.0 degrees, counter-clockwise. + sal_Int32 nExpectedRotateAngle = 45; + uno::Sequence<beans::PropertyValue> aFilterData{ + comphelper::makePropertyValue("Watermark", OUString("X")), + comphelper::makePropertyValue("WatermarkRotateAngle", nExpectedRotateAngle * 10), + }; + uno::Sequence<beans::PropertyValue> aDescriptor{ + comphelper::makePropertyValue("FilterName", OUString("writer_pdf_Export")), + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xOutputStream), + }; + xFilter->filter(aDescriptor); + + // Then make sure that the watermark rotation angle is correct: + std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument + = pPDFium->openDocument(aStream.GetData(), aStream.GetSize(), OString()); + CPPUNIT_ASSERT(pPdfDocument); + std::unique_ptr<vcl::pdf::PDFiumPage> pPage = pPdfDocument->openPage(0); + CPPUNIT_ASSERT_EQUAL(1, pPage->getObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(0); + CPPUNIT_ASSERT_EQUAL(1, pPageObject->getFormObjectCount()); + std::unique_ptr<vcl::pdf::PDFiumPageObject> 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); +} +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |