/* -*- 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(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<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
if (!pPDFium)
return;
uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
= xml::crypto::SEInitializer::create(m_xContext);
uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
= xSEInitializer->createSecurityContext(OUString());
uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment
= xSecurityContext->getSecurityEnvironment();
uno::Sequence<beans::PropertyValue> 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<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
uno::Reference<document::XFilter> xFilter(
xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), 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(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<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:
loadFromURL(u"private:factory/swriter"_ustr);
// When inserting a 267 points wide PDF image into the document:
uno::Sequence<beans::PropertyValue> 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<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(u"Graphic"_ustr).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(u"com.sun.star.document.PDFFilter"_ustr), 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(
u"ExportNotesInMargin"_ustr, commentsInMarginEnabled) };
uno::Sequence<beans::PropertyValue> 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<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;
loadFromURL(u"private:factory/swriter"_ustr);
// When exporting that as PDF with a red watermark:
uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
uno::Reference<document::XFilter> xFilter(
xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), 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(u"Watermark"_ustr, u"X"_ustr),
comphelper::makePropertyValue(u"WatermarkColor"_ustr, static_cast<sal_Int32>(0xff0000)),
};
uno::Sequence<beans::PropertyValue> 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<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;
loadFromURL(u"private:factory/swriter"_ustr);
// When exporting that as PDF with a 100pt-sized watermark:
uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
uno::Reference<document::XFilter> xFilter(
xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), 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(u"Watermark"_ustr, u"X"_ustr),
comphelper::makePropertyValue(u"WatermarkFontHeight"_ustr, nExpectedFontSize),
};
uno::Sequence<beans::PropertyValue> 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<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;
loadFromURL(u"private:factory/swriter"_ustr);
// When exporting that as PDF with a serif watermark:
uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
uno::Reference<document::XFilter> xFilter(
xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), 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(u"Liberation Serif"_ustr);
uno::Sequence<beans::PropertyValue> aFilterData{
comphelper::makePropertyValue(u"Watermark"_ustr, u"X"_ustr),
comphelper::makePropertyValue(u"WatermarkFontName"_ustr, aExpectedFontName),
};
uno::Sequence<beans::PropertyValue> 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<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;
loadFromURL(u"private:factory/swriter"_ustr);
// When exporting that as PDF with a rotated watermark:
uno::Reference<css::lang::XMultiServiceFactory> xFactory = getMultiServiceFactory();
uno::Reference<document::XFilter> xFilter(
xFactory->createInstance(u"com.sun.star.document.PDFFilter"_ustr), 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(u"Watermark"_ustr, u"X"_ustr),
comphelper::makePropertyValue(u"WatermarkRotateAngle"_ustr, nExpectedRotateAngle * 10),
};
uno::Sequence<beans::PropertyValue> 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<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);
}
#ifdef UNX
CPPUNIT_TEST_FIXTURE(Test, testSignCertificatePEM)
{
// Given an empty document:
std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
if (!pPDFium)
return;
uno::Reference<xml::crypto::XSEInitializer> xSEInitializer
= xml::crypto::SEInitializer::create(m_xContext);
uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext
= xSEInitializer->createSecurityContext(OUString());
uno::Reference<xml::crypto::XSecurityEnvironment> 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<beans::PropertyValue> 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<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 configured 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: */
↑ V530 The return value of function 'loadFromFile' is required to be utilized.