summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--vcl/CppunitTest_vcl_pdfexport2.mk52
-rw-r--r--vcl/Module_vcl.mk1
-rw-r--r--vcl/qa/cppunit/pdfexport/pdfexport.cxx4732
-rw-r--r--vcl/qa/cppunit/pdfexport/pdfexport2.cxx4802
4 files changed, 4856 insertions, 4731 deletions
diff --git a/vcl/CppunitTest_vcl_pdfexport2.mk b/vcl/CppunitTest_vcl_pdfexport2.mk
new file mode 100644
index 000000000000..5574f515a7b0
--- /dev/null
+++ b/vcl/CppunitTest_vcl_pdfexport2.mk
@@ -0,0 +1,52 @@
+# -*- 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_pdfexport2))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,vcl_pdfexport2, \
+ vcl/qa/cppunit/pdfexport/pdfexport2 \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,vcl_pdfexport2))
+
+$(eval $(call gb_CppunitTest_use_libraries,vcl_pdfexport2, \
+ basegfx \
+ comphelper \
+ cppu \
+ cppuhelper \
+ sal \
+ subsequenttest \
+ test \
+ unotest \
+ utl \
+ tl \
+ vcl \
+ xmlsecurity \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,vcl_pdfexport2, \
+ boost_headers \
+ $(if $(filter PDFIUM,$(BUILD_TYPE)),pdfium) \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,vcl_pdfexport2))
+
+$(eval $(call gb_CppunitTest_use_ure,vcl_pdfexport2))
+$(eval $(call gb_CppunitTest_use_vcl,vcl_pdfexport2))
+
+$(eval $(call gb_CppunitTest_use_rdb,vcl_pdfexport2,services))
+
+$(eval $(call gb_CppunitTest_use_configuration,vcl_pdfexport2))
+
+# assert if font/glyph fallback occurs
+$(eval $(call gb_CppunitTest_set_non_application_font_use,vcl_pdfexport2,abort))
+
+$(eval $(call gb_CppunitTest_use_more_fonts,vcl_pdfexport2))
+
+# vim: set noet sw=4 ts=4:
diff --git a/vcl/Module_vcl.mk b/vcl/Module_vcl.mk
index dd66a86dbf73..19ab27106ad0 100644
--- a/vcl/Module_vcl.mk
+++ b/vcl/Module_vcl.mk
@@ -286,6 +286,7 @@ endif
ifneq (,$(filter PDFIUM,$(BUILD_TYPE)))
$(eval $(call gb_Module_add_slowcheck_targets,vcl,\
CppunitTest_vcl_pdfexport \
+ CppunitTest_vcl_pdfexport2 \
CppunitTest_vcl_filter_ipdf \
))
endif
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport.cxx b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
index 89e2f4990973..fdcc1730b6a8 100644
--- a/vcl/qa/cppunit/pdfexport/pdfexport.cxx
+++ b/vcl/qa/cppunit/pdfexport/pdfexport.cxx
@@ -11,41 +11,21 @@
#include <memory>
#include <string_view>
-#include <type_traits>
-#include <config_features.h>
#include <config_fonts.h>
-#include <osl/process.h>
-#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XStorable.hpp>
#include <com/sun/star/view/XPrintable.hpp>
-#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
-#include <com/sun/star/util/XRefreshable.hpp>
-#include <com/sun/star/beans/XPropertySet.hpp>
-#include <com/sun/star/drawing/XShape.hpp>
-#include <com/sun/star/text/XTextDocument.hpp>
-#include <com/sun/star/document/XFilter.hpp>
-#include <com/sun/star/document/XExporter.hpp>
-#include <com/sun/star/io/XOutputStream.hpp>
-
-#include <comphelper/scopeguard.hxx>
-#include <comphelper/processfactory.hxx>
+
#include <comphelper/propertysequence.hxx>
#include <test/unoapi_test.hxx>
#include <unotools/mediadescriptor.hxx>
#include <unotools/tempfile.hxx>
#include <vcl/filter/pdfdocument.hxx>
#include <tools/zcodec.hxx>
-#include <tools/XmlWalker.hxx>
-#include <vcl/graphicfilter.hxx>
-#include <basegfx/matrix/b2dhommatrix.hxx>
-#include <unotools/streamwrap.hxx>
-#include <rtl/math.hxx>
#include <o3tl/string_view.hxx>
#include <vcl/filter/PDFiumLibrary.hxx>
-#include <comphelper/propertyvalue.hxx>
using namespace ::com::sun::star;
@@ -2009,4716 +1989,6 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf115967)
CPPUNIT_ASSERT_EQUAL(OUString("m=750abc"), sText);
}
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf124272)
-{
- // Import the bugdoc and export as PDF.
- vcl::filter::PDFDocument aDocument;
- load(u"tdf124272.odt", aDocument);
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- // The page has a stream.
- vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
- CPPUNIT_ASSERT(pContents);
- vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- // Uncompress it.
- SvMemoryStream aUncompressed;
- ZCodec aZCodec;
- aZCodec.BeginCompression();
- rObjectStream.Seek(0);
- aZCodec.Decompress(rObjectStream, aUncompressed);
- CPPUNIT_ASSERT(aZCodec.EndCompression());
-
- OString aBitmap("Q q 299.899 782.189 m\n"
- "55.2 435.889 l 299.899 435.889 l 299.899 782.189 l\n"
- "h");
-
- auto pStart = static_cast<const char*>(aUncompressed.GetData());
- const char* pEnd = pStart + aUncompressed.GetSize();
- auto it = std::search(pStart, pEnd, aBitmap.getStr(), aBitmap.getStr() + aBitmap.getLength());
- CPPUNIT_ASSERT(it != pEnd);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf121615)
-{
- vcl::filter::PDFDocument aDocument;
- load(u"tdf121615.odt", aDocument);
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- // Get access to the only image on the only page.
- vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
- CPPUNIT_ASSERT(pResources);
- auto pXObjects
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
- CPPUNIT_ASSERT(pXObjects);
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
- vcl::filter::PDFObjectElement* pXObject
- = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
- CPPUNIT_ASSERT(pXObject);
- vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
-
- // Load the embedded image.
- rObjectStream.Seek(0);
- GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
- Graphic aGraphic;
- sal_uInt16 format;
- ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
- GRFILTER_FORMAT_DONTKNOW, &format);
- CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
-
- // The image should be grayscale 8bit JPEG.
- sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
- CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
- CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
- BitmapEx aBitmap = aGraphic.GetBitmapEx();
- CPPUNIT_ASSERT_EQUAL(tools::Long(200), aBitmap.GetSizePixel().Width());
- CPPUNIT_ASSERT_EQUAL(tools::Long(300), aBitmap.GetSizePixel().Height());
- CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, aBitmap.getPixelFormat());
- // tdf#121615 was caused by broken handling of data width with 8bit color,
- // so the test image has some black in the bottomright corner, check it's there
- CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(0, 0));
- CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(0, 299));
- CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(199, 0));
- CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmap.GetPixelColor(199, 299));
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf141171)
-{
- vcl::filter::PDFDocument aDocument;
- load(u"tdf141171.odt", aDocument);
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- // Get access to the only image on the only page.
- vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
- CPPUNIT_ASSERT(pResources);
- auto pXObjects
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
- CPPUNIT_ASSERT(pXObjects);
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
- vcl::filter::PDFObjectElement* pXObject
- = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
- CPPUNIT_ASSERT(pXObject);
- vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
-
- // Load the embedded image.
- rObjectStream.Seek(0);
- GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
- Graphic aGraphic;
- sal_uInt16 format;
- ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
- GRFILTER_FORMAT_DONTKNOW, &format);
- CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
-
- // The image should be grayscale 8bit JPEG.
- sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
- CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
- CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
- BitmapEx aBitmap = aGraphic.GetBitmapEx();
- Size aSize = aBitmap.GetSizePixel();
- CPPUNIT_ASSERT_EQUAL(tools::Long(878), aSize.Width());
- CPPUNIT_ASSERT_EQUAL(tools::Long(127), aSize.Height());
- CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, aBitmap.getPixelFormat());
-
- for (tools::Long nX = 0; nX < aSize.Width(); ++nX)
- {
- for (tools::Long nY = 0; nY < aSize.Height(); ++nY)
- {
- // Check all pixels in the image are white
- // Without the fix in place, this test would have failed with
- // - Expected: Color: R:255 G:255 B:255 A:0
- // - Actual : Color: R:0 G:0 B:0 A:0
- CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(nX, nY));
- }
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf129085)
-{
- vcl::filter::PDFDocument aDocument;
- load(u"tdf129085.docx", aDocument);
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- // Get access to the only image on the only page.
- vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
- CPPUNIT_ASSERT(pResources);
- auto pXObjects
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
-
- // Without the fix in place, this test would have failed here
- CPPUNIT_ASSERT(pXObjects);
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
- vcl::filter::PDFObjectElement* pXObject
- = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
- CPPUNIT_ASSERT(pXObject);
- vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
-
- // Load the embedded image.
- rObjectStream.Seek(0);
- GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
- Graphic aGraphic;
- sal_uInt16 format;
- ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
- GRFILTER_FORMAT_DONTKNOW, &format);
- CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
-
- sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
- CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
- CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
- BitmapEx aBitmap = aGraphic.GetBitmapEx();
- CPPUNIT_ASSERT_EQUAL(tools::Long(884), aBitmap.GetSizePixel().Width());
- CPPUNIT_ASSERT_EQUAL(tools::Long(925), aBitmap.GetSizePixel().Height());
- CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aBitmap.getPixelFormat());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTocLink)
-{
- // Load the Writer document.
- loadFromURL(u"toc-link.fodt");
-
- // Update the ToC.
- uno::Reference<text::XDocumentIndexesSupplier> xDocumentIndexesSupplier(mxComponent,
- uno::UNO_QUERY);
- CPPUNIT_ASSERT(xDocumentIndexesSupplier.is());
-
- uno::Reference<util::XRefreshable> xToc(
- xDocumentIndexesSupplier->getDocumentIndexes()->getByIndex(0), uno::UNO_QUERY);
- CPPUNIT_ASSERT(xToc.is());
-
- xToc->refresh();
-
- // Save as PDF.
- save("writer_pdf_Export");
-
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
-
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- // Ensure there is a link on the first page (in the ToC).
- // Without the accompanying fix in place, this test would have failed, as the page contained no
- // links.
- CPPUNIT_ASSERT(pPdfPage->hasLinks());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testReduceSmallImage)
-{
- // Load the Writer document.
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- saveAsPDF(u"reduce-small-image.fodt");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pPageObject->getType());
-
- // Make sure we don't scale down a tiny bitmap.
- std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
- CPPUNIT_ASSERT(pBitmap);
- int nWidth = pBitmap->getWidth();
- int nHeight = pBitmap->getHeight();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 16
- // - Actual : 6
- // i.e. the image was scaled down to 300 DPI, even if it had tiny size.
- CPPUNIT_ASSERT_EQUAL(16, nWidth);
- CPPUNIT_ASSERT_EQUAL(16, nHeight);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf114256)
-{
- aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
- saveAsPDF(u"tdf114256.ods");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- // Without the fix in place, this test would have failed with
- // - Expected: 13
- // - Actual : 0
- CPPUNIT_ASSERT_EQUAL(13, pPdfPage->getObjectCount());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf150931)
-{
- aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
- saveAsPDF(u"tdf150931.ods");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- int nPageObjectCount = pPdfPage->getObjectCount();
- // Without the fix in place, this test would have failed with
- // - Expected: 15
- // - Actual : 16
- CPPUNIT_ASSERT_EQUAL(16, nPageObjectCount);
-
- int nYellowPathCount = 0;
- int nBlackPathCount = 0;
- int nGrayPathCount = 0;
- int nRedPathCount = 0;
- for (int i = 0; i < nPageObjectCount; ++i)
- {
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPdfPageObject = pPdfPage->getObject(i);
- if (pPdfPageObject->getType() != vcl::pdf::PDFPageObjectType::Path)
- continue;
-
- int nSegments = pPdfPageObject->getPathSegmentCount();
- CPPUNIT_ASSERT_EQUAL(5, nSegments);
-
- if (pPdfPageObject->getFillColor() == COL_YELLOW)
- ++nYellowPathCount;
- else if (pPdfPageObject->getFillColor() == COL_BLACK)
- ++nBlackPathCount;
- else if (pPdfPageObject->getFillColor() == COL_GRAY)
- ++nGrayPathCount;
- else if (pPdfPageObject->getFillColor() == COL_LIGHTRED)
- ++nRedPathCount;
- }
-
- CPPUNIT_ASSERT_EQUAL(3, nYellowPathCount);
- CPPUNIT_ASSERT_EQUAL(3, nRedPathCount);
- CPPUNIT_ASSERT_EQUAL(3, nGrayPathCount);
- CPPUNIT_ASSERT_EQUAL(3, nBlackPathCount);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf147027)
-{
- // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
- // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
- if (!IsDefaultDPI())
- return;
-
- // Load the Calc document.
- aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
- saveAsPDF(u"tdf147027.ods");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- // Without the fix in place, this test would have failed with
- // - Expected: 778
- // - Actual : 40
- CPPUNIT_ASSERT_EQUAL(778, pPdfPage->getObjectCount());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf135346)
-{
- // Load the Calc document.
- aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
- saveAsPDF(u"tdf135346.ods");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- // Without the fix in place, this test would have failed with
- // - Expected: 56
- // - Actual : 0
- CPPUNIT_ASSERT_EQUAL(56, pPdfPage->getObjectCount());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf147164)
-{
- aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
- saveAsPDF(u"tdf147164.odp");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/1);
- CPPUNIT_ASSERT(pPdfPage);
-
- // Without the fix in place, this test would have failed with
- // - Expected: 22
- // - Actual : 16
- CPPUNIT_ASSERT_EQUAL(22, pPdfPage->getObjectCount());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testReduceImage)
-{
- // Load the Writer document.
- loadFromURL(u"reduce-image.fodt");
-
- // Save as PDF.
- 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);
-
- SvFileStream aOutputStream(maTempFile.GetURL(), StreamMode::WRITE);
- uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aOutputStream));
-
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "ReduceImageResolution", uno::Any(false) } }));
-
- // This is intentionally in an "unlucky" order, output stream comes before filter data.
- uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({
- { "FilterName", uno::Any(OUString("writer_pdf_Export")) },
- { "OutputStream", uno::Any(xOutputStream) },
- { "FilterData", uno::Any(aFilterData) },
- }));
- xFilter->filter(aDescriptor);
- aOutputStream.Close();
-
- // Parse the PDF: get the image.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pPageObject->getType());
-
- // Make sure we don't scale down a bitmap.
- std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
- CPPUNIT_ASSERT(pBitmap);
- int nWidth = pBitmap->getWidth();
- int nHeight = pBitmap->getHeight();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 160
- // - Actual : 6
- // i.e. the image was scaled down even with ReduceImageResolution=false.
- CPPUNIT_ASSERT_EQUAL(160, nWidth);
- CPPUNIT_ASSERT_EQUAL(160, nHeight);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testLinkWrongPage)
-{
- // Import the bugdoc and export as PDF.
- aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
- saveAsPDF(u"link-wrong-page.odp");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- // The document has 2 pages.
- CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
-
- // First page should have 1 link (2nd slide, 1st was hidden).
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- // Without the accompanying fix in place, this test would have failed, as the link of the first
- // page went to the second page due to the hidden first slide.
- CPPUNIT_ASSERT(pPdfPage->hasLinks());
-
- // Second page should have no links (3rd slide).
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage2 = pPdfDocument->openPage(/*nIndex=*/1);
- CPPUNIT_ASSERT(pPdfPage2);
- CPPUNIT_ASSERT(!pPdfPage2->hasLinks());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testLinkWrongPagePartial)
-{
- // Given a Draw document with 3 pages, a link on the 2nd page:
- // When exporting that the 2nd and 3rd page to pdf:
- uno::Sequence<beans::PropertyValue> aFilterData = {
- comphelper::makePropertyValue("PageRange", OUString("2-3")),
- };
- aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"link-wrong-page-partial.odg");
-
- // Then make sure the we have a link on the 1st page, but not on the 2nd one:
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- // Without the accompanying fix in place, this test would have failed, as the link was on the
- // 2nd page instead.
- CPPUNIT_ASSERT(pPdfPage->hasLinks());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage2 = pPdfDocument->openPage(/*nIndex=*/1);
- CPPUNIT_ASSERT(pPdfPage2);
- CPPUNIT_ASSERT(!pPdfPage2->hasLinks());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testPageRange)
-{
- // Given a document with 3 pages:
- // When exporting that document to PDF, skipping the first page:
- aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
- aMediaDescriptor["FilterOptions"]
- <<= OUString("{\"PageRange\":{\"type\":\"string\",\"value\":\"2-\"}}");
- saveAsPDF(u"link-wrong-page-partial.odg");
-
- // Then make sure the resulting PDF has 2 pages:
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 2
- // - Actual : 3
- // i.e. FilterOptions was ignored.
- CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testLargePage)
-{
- // Import the bugdoc and export as PDF.
- aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
- saveAsPDF(u"6m-wide.odg");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- // The document has 1 page.
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- // Check the value (not the unit) of the page size.
- basegfx::B2DSize aSize = pPdfDocument->getPageSize(0);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 8503.94
- // - Actual : 17007.875
- // i.e. the value for 600 cm was larger than the 14 400 limit set in the spec.
- CPPUNIT_ASSERT_DOUBLES_EQUAL(8503.94, aSize.getWidth(), 0.01);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageResourceInlineXObjectRef)
-{
- // Create an empty document.
- mxComponent = loadFromDesktop("private:factory/swriter");
- uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
- uno::Reference<text::XText> xText = xTextDocument->getText();
- uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
-
- // Insert the PDF image.
- uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference<beans::XPropertySet> xGraphicObject(
- xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
- OUString aURL = createFileURL(u"pdf-image-resource-inline-xobject-ref.pdf");
- xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
- uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
- xShape->setSize(awt::Size(1000, 1000));
- uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
- xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
-
- // Save as PDF.
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- // Parse the export result.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
-
- // Make sure that the page -> form -> form has a child image.
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
- // 2: white background and the actual object.
- CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(1);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pFormObject->getType());
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 1
- // - Actual : 0
- // i.e. the sub-form was missing its image.
- CPPUNIT_ASSERT_EQUAL(1, pFormObject->getFormObjectCount());
-
- // Check if the inner form object (original page object in the pdf image) has the correct
- // rotation.
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject = pFormObject->getFormObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pInnerFormObject->getType());
- CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject->getFormObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pImage = pInnerFormObject->getFormObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pImage->getType());
- basegfx::B2DHomMatrix aMat = pInnerFormObject->getMatrix();
- basegfx::B2DTuple aScale;
- basegfx::B2DTuple aTranslate;
- double fRotate = 0;
- double fShearX = 0;
- aMat.decompose(aScale, aTranslate, fRotate, fShearX);
- int nRotateDeg = basegfx::rad2deg(fRotate);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: -90
- // - Actual : 0
- // i.e. rotation was lost on pdf export.
- CPPUNIT_ASSERT_EQUAL(-90, nRotateDeg);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testDefaultVersion)
-{
- // Create an empty document.
- mxComponent = loadFromDesktop("private:factory/swriter");
-
- // Save as PDF.
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- // Parse the export result.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- int nFileVersion = pPdfDocument->getFileVersion();
- CPPUNIT_ASSERT_EQUAL(17, nFileVersion);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testVersion15)
-{
- // Create an empty document.
- mxComponent = loadFromDesktop("private:factory/swriter");
-
- // Save as PDF.
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
- { { "SelectPdfVersion", uno::Any(static_cast<sal_Int32>(15)) } }));
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- aMediaDescriptor["FilterData"] <<= aFilterData;
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- // Parse the export result.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- int nFileVersion = pPdfDocument->getFileVersion();
- CPPUNIT_ASSERT_EQUAL(15, nFileVersion);
-}
-
-// Check round-trip of importing and exporting the PDF with PDFium filter,
-// which imports the PDF document as multiple PDFs as graphic object.
-// Each page in the document has one PDF graphic object which content is
-// the corresponding page in the PDF. When such a document is exported,
-// the PDF graphic gets embedded into the exported PDF document (as a
-// Form XObject).
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testMultiPagePDF)
-{
-// setenv only works on unix based systems
-#ifndef _WIN32
- // We need to enable PDFium import (and make sure to disable after the test)
- bool bResetEnvVar = false;
- if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
- {
- bResetEnvVar = true;
- setenv("LO_IMPORT_USE_PDFIUM", "1", false);
- }
- comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
- if (bResetEnvVar)
- unsetenv("LO_IMPORT_USE_PDFIUM");
- });
-
- // Load the PDF and save as PDF
- vcl::filter::PDFDocument aDocument;
- load(u"SimpleMultiPagePDF.pdf", aDocument);
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages.size());
-
- vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
- CPPUNIT_ASSERT(pResources);
-
- auto pXObjects
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
- CPPUNIT_ASSERT(pXObjects);
-
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3),
- pXObjects->GetItems().size()); // 3 PDFs as Form XObjects
-
- std::vector<OString> rIDs;
- for (auto const& rPair : pXObjects->GetItems())
- {
- rIDs.push_back(rPair.first);
- }
-
- // Let's check the embedded PDF pages - just make sure the size differs,
- // which should indicate we don't have 3 times the same page.
-
- { // embedded PDF page 1
- vcl::filter::PDFObjectElement* pXObject1 = pXObjects->LookupObject(rIDs[0]);
- CPPUNIT_ASSERT(pXObject1);
- CPPUNIT_ASSERT_EQUAL(OString("Im21"), rIDs[0]);
-
- auto pSubtype1 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject1->Lookup("Subtype"));
- CPPUNIT_ASSERT(pSubtype1);
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype1->GetValue());
-
- auto pXObjectResources
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject1->Lookup("Resources"));
- CPPUNIT_ASSERT(pXObjectResources);
- auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pXObjectResources->LookupElement("XObject"));
- CPPUNIT_ASSERT(pXObjectForms);
- vcl::filter::PDFObjectElement* pForm
- = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
- CPPUNIT_ASSERT(pForm);
-
- vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
-
- // Just check that the size of the page stream is what is expected.
- CPPUNIT_ASSERT_EQUAL(sal_uInt64(230), rObjectStream.remainingSize());
- }
-
- { // embedded PDF page 2
- vcl::filter::PDFObjectElement* pXObject2 = pXObjects->LookupObject(rIDs[1]);
- CPPUNIT_ASSERT(pXObject2);
- CPPUNIT_ASSERT_EQUAL(OString("Im27"), rIDs[1]);
-
- auto pSubtype2 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject2->Lookup("Subtype"));
- CPPUNIT_ASSERT(pSubtype2);
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype2->GetValue());
-
- auto pXObjectResources
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject2->Lookup("Resources"));
- CPPUNIT_ASSERT(pXObjectResources);
- auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pXObjectResources->LookupElement("XObject"));
- CPPUNIT_ASSERT(pXObjectForms);
- vcl::filter::PDFObjectElement* pForm
- = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
- CPPUNIT_ASSERT(pForm);
-
- vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
-
- // Just check that the size of the page stream is what is expected
- CPPUNIT_ASSERT_EQUAL(sal_uInt64(309), rObjectStream.remainingSize());
- }
-
- { // embedded PDF page 3
- vcl::filter::PDFObjectElement* pXObject3 = pXObjects->LookupObject(rIDs[2]);
- CPPUNIT_ASSERT(pXObject3);
- CPPUNIT_ASSERT_EQUAL(OString("Im5"), rIDs[2]);
-
- auto pSubtype3 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject3->Lookup("Subtype"));
- CPPUNIT_ASSERT(pSubtype3);
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype3->GetValue());
-
- auto pXObjectResources
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject3->Lookup("Resources"));
- CPPUNIT_ASSERT(pXObjectResources);
- auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pXObjectResources->LookupElement("XObject"));
- CPPUNIT_ASSERT(pXObjectForms);
- vcl::filter::PDFObjectElement* pForm
- = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
- CPPUNIT_ASSERT(pForm);
-
- vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
-
- // Just check that the size of the page stream is what is expected
- CPPUNIT_ASSERT_EQUAL(sal_uInt64(193), rObjectStream.remainingSize());
- }
-#endif
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testFormFontName)
-{
- // Import the bugdoc and export as PDF.
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- saveAsPDF(u"form-font-name.odt");
-
- // Parse the export result with pdfium.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- // The document has one page.
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- // The page has one annotation.
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount());
- std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnot = pPdfPage->getAnnotation(0);
-
- // Examine the default appearance.
- CPPUNIT_ASSERT(pAnnot->hasKey("DA"));
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFObjectType::String, pAnnot->getValueType("DA"));
- OUString aDA = pAnnot->getString("DA");
-
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 0 0 0 rg /TiRo 12 Tf
- // - Actual : 0 0 0 rg /F2 12 Tf
- // i.e. Liberation Serif was exposed as a form font as-is, without picking the closest built-in
- // font.
- CPPUNIT_ASSERT_EQUAL(OUString("0 0 0 rg /TiRo 12 Tf"), aDA);
-}
-
-// Check we don't have duplicated objects when we reexport the PDF multiple
-// times or the size will exponentially increase over time.
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testReexportPDF)
-{
-// setenv only works on unix based systems
-#ifndef _WIN32
- // We need to enable PDFium import (and make sure to disable after the test)
- bool bResetEnvVar = false;
- if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
- {
- bResetEnvVar = true;
- setenv("LO_IMPORT_USE_PDFIUM", "1", false);
- }
- comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
- if (bResetEnvVar)
- unsetenv("LO_IMPORT_USE_PDFIUM");
- });
-
- // Load the PDF and save as PDF
- vcl::filter::PDFDocument aDocument;
- load(u"PDFWithImages.pdf", aDocument);
-
- // Assert that the XObject in the page resources dictionary is a reference XObject.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
-
- // The document has 2 pages.
- CPPUNIT_ASSERT_EQUAL(size_t(2), aPages.size());
-
- // PAGE 1
- {
- vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
- CPPUNIT_ASSERT(pResources);
-
- auto pXObjects
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
- CPPUNIT_ASSERT(pXObjects);
-
- std::vector<OString> rIDs;
- for (auto const& rPair : pXObjects->GetItems())
- rIDs.push_back(rPair.first);
-
- CPPUNIT_ASSERT_EQUAL(size_t(2), rIDs.size());
-
- std::vector<int> aBitmapRefs1;
- std::vector<int> aBitmapRefs2;
-
- {
- // FORM object 1
- OString aID = rIDs[0];
- CPPUNIT_ASSERT_EQUAL(OString("Im14"), aID);
- vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(aID);
- CPPUNIT_ASSERT(pXObject);
-
- auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject->Lookup("Subtype"));
- CPPUNIT_ASSERT(pSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype->GetValue());
-
- auto pInnerResources
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
- CPPUNIT_ASSERT(pInnerResources);
- auto pInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pInnerResources->LookupElement("XObject"));
- CPPUNIT_ASSERT(pInnerXObjects);
- CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects->GetItems().size());
- OString aInnerObjectID = pInnerXObjects->GetItems().begin()->first;
- CPPUNIT_ASSERT_EQUAL(OString("Im15"), aInnerObjectID);
-
- vcl::filter::PDFObjectElement* pInnerXObject
- = pInnerXObjects->LookupObject(aInnerObjectID);
- CPPUNIT_ASSERT(pInnerXObject);
-
- auto pInnerSubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerXObject->Lookup("Subtype"));
- CPPUNIT_ASSERT(pInnerSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pInnerSubtype->GetValue());
-
- auto pInnerInnerResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pInnerXObject->Lookup("Resources"));
- CPPUNIT_ASSERT(pInnerInnerResources);
- auto pInnerInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pInnerInnerResources->LookupElement("XObject"));
- CPPUNIT_ASSERT(pInnerInnerXObjects);
- CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects->GetItems().size());
-
- std::vector<OString> aBitmapIDs1;
- for (auto const& rPair : pInnerInnerXObjects->GetItems())
- aBitmapIDs1.push_back(rPair.first);
-
- {
- CPPUNIT_ASSERT_EQUAL(OString("Im11"), aBitmapIDs1[0]);
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
- pInnerInnerXObjects->LookupElement(aBitmapIDs1[0]));
- CPPUNIT_ASSERT(pRef);
- aBitmapRefs1.push_back(pRef->GetObjectValue());
- CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
-
- vcl::filter::PDFObjectElement* pBitmap
- = pInnerInnerXObjects->LookupObject(aBitmapIDs1[0]);
- CPPUNIT_ASSERT(pBitmap);
- auto pBitmapSubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
- CPPUNIT_ASSERT(pBitmapSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
- }
- {
- CPPUNIT_ASSERT_EQUAL(OString("Im5"), aBitmapIDs1[1]);
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
- pInnerInnerXObjects->LookupElement(aBitmapIDs1[1]));
- CPPUNIT_ASSERT(pRef);
- aBitmapRefs1.push_back(pRef->GetObjectValue());
- CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
-
- vcl::filter::PDFObjectElement* pBitmap
- = pInnerInnerXObjects->LookupObject(aBitmapIDs1[1]);
- CPPUNIT_ASSERT(pBitmap);
- auto pBitmapSubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
- CPPUNIT_ASSERT(pBitmapSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
- }
- }
-
- {
- // FORM object 2
- OString aID = rIDs[1];
- CPPUNIT_ASSERT_EQUAL(OString("Im5"), aID);
- vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(aID);
- CPPUNIT_ASSERT(pXObject);
-
- auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject->Lookup("Subtype"));
- CPPUNIT_ASSERT(pSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype->GetValue());
-
- auto pInnerResources
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
- CPPUNIT_ASSERT(pInnerResources);
- auto pInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pInnerResources->LookupElement("XObject"));
- CPPUNIT_ASSERT(pInnerXObjects);
- CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects->GetItems().size());
- OString aInnerObjectID = pInnerXObjects->GetItems().begin()->first;
- CPPUNIT_ASSERT_EQUAL(OString("Im6"), aInnerObjectID);
-
- vcl::filter::PDFObjectElement* pInnerXObject
- = pInnerXObjects->LookupObject(aInnerObjectID);
- CPPUNIT_ASSERT(pInnerXObject);
-
- auto pInnerSubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerXObject->Lookup("Subtype"));
- CPPUNIT_ASSERT(pInnerSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pInnerSubtype->GetValue());
-
- auto pInnerInnerResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pInnerXObject->Lookup("Resources"));
- CPPUNIT_ASSERT(pInnerInnerResources);
- auto pInnerInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
- pInnerInnerResources->LookupElement("XObject"));
- CPPUNIT_ASSERT(pInnerInnerXObjects);
- CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects->GetItems().size());
-
- std::vector<OString> aBitmapIDs2;
- for (auto const& rPair : pInnerInnerXObjects->GetItems())
- aBitmapIDs2.push_back(rPair.first);
-
- {
- CPPUNIT_ASSERT_EQUAL(OString("Im11"), aBitmapIDs2[0]);
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
- pInnerInnerXObjects->LookupElement(aBitmapIDs2[0]));
- CPPUNIT_ASSERT(pRef);
- aBitmapRefs2.push_back(pRef->GetObjectValue());
- CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
-
- vcl::filter::PDFObjectElement* pBitmap
- = pInnerInnerXObjects->LookupObject(aBitmapIDs2[0]);
- CPPUNIT_ASSERT(pBitmap);
- auto pBitmapSubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
- CPPUNIT_ASSERT(pBitmapSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
- }
- {
- CPPUNIT_ASSERT_EQUAL(OString("Im5"), aBitmapIDs2[1]);
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
- pInnerInnerXObjects->LookupElement(aBitmapIDs2[1]));
- CPPUNIT_ASSERT(pRef);
- aBitmapRefs2.push_back(pRef->GetObjectValue());
- CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
-
- vcl::filter::PDFObjectElement* pBitmap
- = pInnerInnerXObjects->LookupObject(aBitmapIDs2[1]);
- CPPUNIT_ASSERT(pBitmap);
- auto pBitmapSubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
- CPPUNIT_ASSERT(pBitmapSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
- }
- }
- // Ref should point to the same bitmap
- CPPUNIT_ASSERT_EQUAL(aBitmapRefs1[0], aBitmapRefs2[0]);
- CPPUNIT_ASSERT_EQUAL(aBitmapRefs1[1], aBitmapRefs2[1]);
- }
-
-#endif
-}
-
-// Check we correctly copy more complex resources (Fonts describing
-// glyphs in recursive arrays) to the target PDF
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testReexportDocumentWithComplexResources)
-{
-// setenv only works on unix based systems
-#ifndef _WIN32
- // We need to enable PDFium import (and make sure to disable after the test)
- bool bResetEnvVar = false;
- if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
- {
- bResetEnvVar = true;
- setenv("LO_IMPORT_USE_PDFIUM", "1", false);
- }
- comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
- if (bResetEnvVar)
- unsetenv("LO_IMPORT_USE_PDFIUM");
- });
-
- // Load the PDF and save as PDF
- vcl::filter::PDFDocument aDocument;
- load(u"ComplexContentDictionary.pdf", aDocument);
-
- // Assert that the XObject in the page resources dictionary is a reference XObject.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(size_t(1), aPages.size());
-
- // Go directly to the Font object (24 0) (number could change if we change how PDF export works)
- auto pFont = aDocument.LookupObject(24);
- CPPUNIT_ASSERT(pFont);
-
- // Check it is the Font object (Type = Font)
- auto pName
- = dynamic_cast<vcl::filter::PDFNameElement*>(pFont->GetDictionary()->LookupElement("Type"));
- CPPUNIT_ASSERT(pName);
- CPPUNIT_ASSERT_EQUAL(OString("Font"), pName->GetValue());
-
- // Check BaseFont is what we expect
- auto pBaseFont = dynamic_cast<vcl::filter::PDFNameElement*>(
- pFont->GetDictionary()->LookupElement("BaseFont"));
- CPPUNIT_ASSERT(pBaseFont);
- CPPUNIT_ASSERT_EQUAL(OString("HOTOMR+Calibri,Italic"), pBaseFont->GetValue());
-
- // Check and get the W array
- auto pWArray
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pFont->GetDictionary()->LookupElement("W"));
- CPPUNIT_ASSERT(pWArray);
- CPPUNIT_ASSERT_EQUAL(size_t(26), pWArray->GetElements().size());
-
- // Check the content of W array
- // ObjectCopier didn't copy this array correctly and the document
- // had glyphs at the wrong places
- {
- // first 2 elements
- auto pNumberAtIndex0 = dynamic_cast<vcl::filter::PDFNumberElement*>(pWArray->GetElement(0));
- CPPUNIT_ASSERT(pNumberAtIndex0);
- CPPUNIT_ASSERT_EQUAL(3.0, pNumberAtIndex0->GetValue());
-
- auto pArrayAtIndex1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pWArray->GetElement(1));
- CPPUNIT_ASSERT(pArrayAtIndex1);
- CPPUNIT_ASSERT_EQUAL(size_t(1), pArrayAtIndex1->GetElements().size());
-
- {
- auto pNumber
- = dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex1->GetElement(0));
- CPPUNIT_ASSERT(pNumber);
- CPPUNIT_ASSERT_EQUAL(226.0, pNumber->GetValue());
- }
-
- // last 2 elements
- auto pNumberAtIndex24
- = dynamic_cast<vcl::filter::PDFNumberElement*>(pWArray->GetElement(24));
- CPPUNIT_ASSERT(pNumberAtIndex24);
- CPPUNIT_ASSERT_EQUAL(894.0, pNumberAtIndex24->GetValue());
-
- auto pArrayAtIndex25 = dynamic_cast<vcl::filter::PDFArrayElement*>(pWArray->GetElement(25));
- CPPUNIT_ASSERT(pArrayAtIndex25);
- CPPUNIT_ASSERT_EQUAL(size_t(2), pArrayAtIndex25->GetElements().size());
-
- {
- auto pNumber1
- = dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex25->GetElement(0));
- CPPUNIT_ASSERT(pNumber1);
- CPPUNIT_ASSERT_EQUAL(303.0, pNumber1->GetValue());
-
- auto pNumber2
- = dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex25->GetElement(1));
- CPPUNIT_ASSERT(pNumber2);
- CPPUNIT_ASSERT_EQUAL(303.0, pNumber2->GetValue());
- }
- }
-#endif
-}
-
-// Tests that at export the PDF has the PDF/UA metadata properly set
-// when we enable PDF/UA support.
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfUaMetadata)
-{
- // Import a basic document (document doesn't really matter)
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"BrownFoxLazyDog.odt");
-
- // Parse the export result.
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- auto* pCatalog = aDocument.GetCatalog();
- CPPUNIT_ASSERT(pCatalog);
- auto* pCatalogDictionary = pCatalog->GetDictionary();
- CPPUNIT_ASSERT(pCatalogDictionary);
- auto* pMetadataObject = pCatalogDictionary->LookupObject("Metadata");
- CPPUNIT_ASSERT(pMetadataObject);
- auto* pMetadataDictionary = pMetadataObject->GetDictionary();
- auto* pType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pMetadataDictionary->LookupElement("Type"));
- CPPUNIT_ASSERT(pType);
- CPPUNIT_ASSERT_EQUAL(OString("Metadata"), pType->GetValue());
-
- auto* pStreamObject = pMetadataObject->GetStream();
- CPPUNIT_ASSERT(pStreamObject);
- auto& rStream = pStreamObject->GetMemory();
- rStream.Seek(0);
-
- // Search for the PDF/UA marker in the metadata
-
- tools::XmlWalker aWalker;
- CPPUNIT_ASSERT(aWalker.open(&rStream));
- CPPUNIT_ASSERT_EQUAL(OString("xmpmeta"), aWalker.name());
-
- bool bPdfUaMarkerFound = false;
- OString aPdfUaPart;
-
- aWalker.children();
- while (aWalker.isValid())
- {
- if (aWalker.name() == "RDF"
- && aWalker.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
- {
- aWalker.children();
- while (aWalker.isValid())
- {
- if (aWalker.name() == "Description"
- && aWalker.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
- {
- aWalker.children();
- while (aWalker.isValid())
- {
- if (aWalker.name() == "part"
- && aWalker.namespaceHref() == "http://www.aiim.org/pdfua/ns/id/")
- {
- aPdfUaPart = aWalker.content();
- bPdfUaMarkerFound = true;
- }
- aWalker.next();
- }
- aWalker.parent();
- }
- aWalker.next();
- }
- aWalker.parent();
- }
- aWalker.next();
- }
- aWalker.parent();
-
- CPPUNIT_ASSERT(bPdfUaMarkerFound);
- CPPUNIT_ASSERT_EQUAL(OString("1"), aPdfUaPart);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf139736)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
- { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"tdf139736-1.odt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
- CPPUNIT_ASSERT(pContents);
- vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- // Uncompress it.
- SvMemoryStream aUncompressed;
- ZCodec aZCodec;
- aZCodec.BeginCompression();
- rObjectStream.Seek(0);
- aZCodec.Decompress(rObjectStream, aUncompressed);
- CPPUNIT_ASSERT(aZCodec.EndCompression());
-
- auto pStart = static_cast<const char*>(aUncompressed.GetData());
- const char* const pEnd = pStart + aUncompressed.GetSize();
-
- enum
- {
- Default,
- Artifact,
- ArtifactProps1,
- ArtifactProps2,
- Tagged
- } state
- = Default;
-
- auto nLine(0);
- auto nTagged(0);
- auto nArtifacts(0);
- while (true)
- {
- ++nLine;
- auto const pLine = ::std::find(pStart, pEnd, '\n');
- if (pLine == pEnd)
- {
- break;
- }
- std::string_view const line(pStart, pLine - pStart);
- pStart = pLine + 1;
- if (!line.empty() && line[0] != '%')
- {
- ::std::cerr << nLine << ": " << line << "\n";
- if (line == "/Artifact BMC")
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Artifact;
- ++nArtifacts;
- }
- else if (o3tl::starts_with(line, "/Artifact <<"))
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- // check header/footer properties
- CPPUNIT_ASSERT_EQUAL(std::string_view("/Type/Pagination"), line.substr(12));
- state = ArtifactProps1;
- ++nArtifacts;
- }
- else if (state == ArtifactProps1)
- {
- CPPUNIT_ASSERT_EQUAL(std::string_view("/Subtype/Header"), line);
- state = ArtifactProps2;
- }
- else if (state == ArtifactProps2 && line == ">> BDC")
- {
- state = Artifact;
- }
- else if (line == "/Standard<</MCID 0>>BDC")
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Tagged;
- ++nTagged;
- }
- else if (line == "EMC")
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
- state = Default;
- }
- else if (nLine > 1) // first line is expected "0.1 w"
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged)>(1), nTagged); // text in body
- // 1 image and 1 frame and 1 header text; arbitrary number of aux stuff like borders
- CPPUNIT_ASSERT(nArtifacts >= 3);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf152231)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
- { "ExportNotesInMargin", uno::Any(true) },
- { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"tdf152231.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
- CPPUNIT_ASSERT(pContents);
- vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- // Uncompress it.
- SvMemoryStream aUncompressed;
- ZCodec aZCodec;
- aZCodec.BeginCompression();
- rObjectStream.Seek(0);
- aZCodec.Decompress(rObjectStream, aUncompressed);
- CPPUNIT_ASSERT(aZCodec.EndCompression());
-
- auto pStart = static_cast<const char*>(aUncompressed.GetData());
- const char* const pEnd = pStart + aUncompressed.GetSize();
-
- enum
- {
- Default,
- Artifact,
- Tagged
- } state
- = Default;
-
- auto nLine(0);
- auto nTagged(0);
- auto nArtifacts(0);
- while (true)
- {
- ++nLine;
- auto const pLine = ::std::find(pStart, pEnd, '\n');
- if (pLine == pEnd)
- {
- break;
- }
- std::string_view const line(pStart, pLine - pStart);
- pStart = pLine + 1;
- if (!line.empty() && line[0] != '%')
- {
- ::std::cerr << nLine << ": " << line << "\n";
- if (line == "/Artifact BMC")
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Artifact;
- ++nArtifacts;
- }
- else if (o3tl::starts_with(line, "/Standard<</MCID "))
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Tagged;
- ++nTagged;
- }
- else if (line == "EMC")
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
- state = Default;
- }
- else if (nLine > 1) // first line is expected "0.1 w"
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
- CPPUNIT_ASSERT(nTagged >= 12); // text in body
- // 1 annotation
- CPPUNIT_ASSERT(nArtifacts >= 1);
-
- auto nPara(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject)
- continue;
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- if (pType && pType->GetValue() == "StructElem")
- {
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
- if (pS && pS->GetValue() == "Standard")
- {
- ++nPara;
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject->Lookup("K"));
- CPPUNIT_ASSERT(pKids);
- // one problem was that some StructElem were missing kids
- CPPUNIT_ASSERT(!pKids->GetElements().empty());
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nPara)>(12), nPara);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf152235)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
- { { "PDFUACompliance", uno::Any(true) },
- { "Watermark", uno::Any(OUString("kendy")) },
- // need to set a font to avoid assertions about missing "Helvetica"
- { "WatermarkFontName", uno::Any(OUString("Liberation Sans")) },
- { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- mxComponent = loadFromDesktop("private:factory/swriter");
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
- CPPUNIT_ASSERT(pContents);
- vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- // Uncompress it.
- SvMemoryStream aUncompressed;
- ZCodec aZCodec;
- aZCodec.BeginCompression();
- rObjectStream.Seek(0);
- aZCodec.Decompress(rObjectStream, aUncompressed);
- CPPUNIT_ASSERT(aZCodec.EndCompression());
-
- auto pStart = static_cast<const char*>(aUncompressed.GetData());
- const char* const pEnd = pStart + aUncompressed.GetSize();
-
- enum
- {
- Default,
- Artifact,
- Tagged
- } state
- = Default;
-
- auto nLine(0);
- auto nTagged(0);
- auto nArtifacts(0);
- while (true)
- {
- ++nLine;
- auto const pLine = ::std::find(pStart, pEnd, '\n');
- if (pLine == pEnd)
- {
- break;
- }
- std::string_view const line(pStart, pLine - pStart);
- pStart = pLine + 1;
- if (!line.empty() && line[0] != '%')
- {
- ::std::cerr << nLine << ": " << line << "\n";
- if (o3tl::starts_with(line, "/Artifact "))
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Artifact;
- ++nArtifacts;
- }
- else if (o3tl::starts_with(line, "/Standard<</MCID "))
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Tagged;
- ++nTagged;
- }
- else if (line == "EMC")
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
- state = Default;
- }
- else if (nLine > 1) // first line is expected "0.1 w"
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
- CPPUNIT_ASSERT(nTagged >= 0); // text in body
- CPPUNIT_ASSERT(nArtifacts >= 2); // 1 watermark + 1 other thing
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf149140)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"TableTH_test_LibreOfficeWriter7.3.3_HeaderRow-HeadersInTopRow.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- int nTH(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject)
- continue;
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- if (pType && pType->GetValue() == "StructElem")
- {
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
- if (pS && pS->GetValue() == "TH")
- {
- int nTable(0);
- auto pAttrs = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject->Lookup("A"));
- CPPUNIT_ASSERT(pAttrs != nullptr);
- for (const auto& rAttrRef : pAttrs->GetElements())
- {
- auto pAttrDict = dynamic_cast<vcl::filter::PDFDictionaryElement*>(rAttrRef);
- CPPUNIT_ASSERT(pAttrDict != nullptr);
- auto pOwner
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAttrDict->LookupElement("O"));
- CPPUNIT_ASSERT(pOwner != nullptr);
- if (pOwner->GetValue() == "Table")
- {
- auto pScope = dynamic_cast<vcl::filter::PDFNameElement*>(
- pAttrDict->LookupElement("Scope"));
- CPPUNIT_ASSERT(pScope != nullptr);
- CPPUNIT_ASSERT_EQUAL(OString("Column"), pScope->GetValue());
- ++nTable;
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(1), nTable);
- ++nTH;
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(6), nTH);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testNestedSection)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"nestedsection.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // the assert needs 2 follows to reproduce => 3 pages
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages.size());
-
- auto nDoc(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructElem")
- {
- auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
- if (pS1 && pS1->GetValue() == "Document")
- {
- auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
- CPPUNIT_ASSERT(pKids1);
- // assume there are no MCID ref at this level
- auto pKids1v = pKids1->GetElements();
- auto pRefKid10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[0]);
- CPPUNIT_ASSERT(pRefKid10);
- auto pObject10 = pRefKid10->LookupObject();
- CPPUNIT_ASSERT(pObject10);
- auto pType10
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10->GetValue());
- auto pS10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Sect"), pS10->GetValue());
-
- auto pKids10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10->Lookup("K"));
- CPPUNIT_ASSERT(pKids10);
- // assume there are no MCID ref at this level
- auto pKids10v = pKids10->GetElements();
-
- auto pRefKid100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[0]);
- CPPUNIT_ASSERT(pRefKid100);
- auto pObject100 = pRefKid100->LookupObject();
- CPPUNIT_ASSERT(pObject100);
- auto pType100
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100->GetValue());
- auto pS100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS100->GetValue());
-
- auto pRefKid101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[1]);
- CPPUNIT_ASSERT(pRefKid101);
- auto pObject101 = pRefKid101->LookupObject();
- CPPUNIT_ASSERT(pObject101);
- auto pType101
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType101->GetValue());
- auto pS101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS101->GetValue());
-
- auto pRefKid102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[2]);
- CPPUNIT_ASSERT(pRefKid102);
- auto pObject102 = pRefKid102->LookupObject();
- CPPUNIT_ASSERT(pObject102);
- auto pType102
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102->GetValue());
- auto pS102 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Sect"), pS102->GetValue());
-
- auto pKids102
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102->Lookup("K"));
- CPPUNIT_ASSERT(pKids102);
- // assume there are no MCID ref at this level
- auto pKids102v = pKids102->GetElements();
-
- auto pRefKid1020 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids102v[0]);
- CPPUNIT_ASSERT(pRefKid1020);
- auto pObject1020 = pRefKid1020->LookupObject();
- CPPUNIT_ASSERT(pObject1020);
- auto pType1020
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1020->GetValue());
- auto pS1020 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS1020->GetValue());
-
- CPPUNIT_ASSERT_EQUAL(size_t(1), pKids102v.size());
-
- auto pRefKid103 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[3]);
- CPPUNIT_ASSERT(pRefKid103);
- auto pObject103 = pRefKid103->LookupObject();
- CPPUNIT_ASSERT(pObject103);
- auto pType103
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103->GetValue());
- auto pS103 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS103->GetValue());
-
- auto pRefKid104 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[4]);
- CPPUNIT_ASSERT(pRefKid104);
- auto pObject104 = pRefKid104->LookupObject();
- CPPUNIT_ASSERT(pObject104);
- auto pType104
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject104->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType104->GetValue());
- auto pS104 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject104->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS104->GetValue());
-
- CPPUNIT_ASSERT_EQUAL(size_t(5), pKids10v.size());
-
- auto pRefKid11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[1]);
- CPPUNIT_ASSERT(pRefKid11);
- auto pObject11 = pRefKid11->LookupObject();
- CPPUNIT_ASSERT(pObject11);
- auto pType11
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11->GetValue());
- auto pS11 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS11->GetValue());
-
- CPPUNIT_ASSERT_EQUAL(size_t(2), pKids1v.size());
- ++nDoc;
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDoc)>(1), nDoc);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157817)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"SimpleTOC.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPages.size());
-
- vcl::filter::PDFObjectElement* pTOC(nullptr);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructElem")
- {
- auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
- if (pS1 && pS1->GetValue() == "TOC")
- {
- pTOC = pObject1;
- }
- }
- }
- CPPUNIT_ASSERT(pTOC);
-
- auto pKidsT = dynamic_cast<vcl::filter::PDFArrayElement*>(pTOC->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT);
- // assume there are no MCID ref at this level
- auto pKidsTv = pKidsT->GetElements();
- auto pRefKidT0 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[0]);
- CPPUNIT_ASSERT(pRefKidT0);
- auto pObjectT0 = pRefKidT0->LookupObject();
- CPPUNIT_ASSERT(pObjectT0);
- auto pTypeT0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT0->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT0->GetValue());
- auto pST0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT0->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Caption"), pST0->GetValue());
-
- auto pKidsT0 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT0->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT0);
- auto pKidsT0v = pKidsT0->GetElements();
- auto pRefKidT00 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT0v[0]);
- CPPUNIT_ASSERT(pRefKidT00);
- auto pObjectT00 = pRefKidT00->LookupObject();
- CPPUNIT_ASSERT(pObjectT00);
- auto pTypeT00 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT00->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT00->GetValue());
- auto pST00 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT00->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Contents#20Heading"), pST00->GetValue());
-
- auto pRefKidT1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
- CPPUNIT_ASSERT(pRefKidT1);
- auto pObjectT1 = pRefKidT1->LookupObject();
- CPPUNIT_ASSERT(pObjectT1);
- auto pTypeT1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT1->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT1->GetValue());
- auto pST1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT1->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST1->GetValue());
-
- auto pKidsT1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT1->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT1);
- auto pKidsT1v = pKidsT1->GetElements();
-
- auto pRefKidT10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT1v[0]);
- CPPUNIT_ASSERT(pRefKidT10);
- auto pObjectT10 = pRefKidT10->LookupObject();
- CPPUNIT_ASSERT(pObjectT10);
- auto pTypeT10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT10->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT10->GetValue());
- auto pST10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT10->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST10->GetValue());
-
- auto pKidsT10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT10->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT10);
- auto pKidsT10v = pKidsT10->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT10v.size());
-
- // there is one and only one Link
- auto pRefKidT100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT10v[0]);
- CPPUNIT_ASSERT(pRefKidT100);
- auto pObjectT100 = pRefKidT100->LookupObject();
- CPPUNIT_ASSERT(pObjectT100);
- auto pTypeT100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT100->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT100->GetValue());
- auto pST100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT100->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pST100->GetValue());
-
- auto pRefKidT2 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
- CPPUNIT_ASSERT(pRefKidT2);
- auto pObjectT2 = pRefKidT2->LookupObject();
- CPPUNIT_ASSERT(pObjectT2);
- auto pTypeT2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT2->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT2->GetValue());
- auto pST2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT2->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST2->GetValue());
-
- auto pKidsT2 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT2->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT2);
- auto pKidsT2v = pKidsT2->GetElements();
-
- auto pRefKidT20 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT2v[0]);
- CPPUNIT_ASSERT(pRefKidT20);
- auto pObjectT20 = pRefKidT20->LookupObject();
- CPPUNIT_ASSERT(pObjectT20);
- auto pTypeT20 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT20->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT20->GetValue());
- auto pST20 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT20->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST20->GetValue());
-
- auto pKidsT20 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT20->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT20);
- auto pKidsT20v = pKidsT20->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT20v.size());
-
- // there is one and only one Link
- auto pRefKidT200 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT20v[0]);
- CPPUNIT_ASSERT(pRefKidT200);
- auto pObjectT200 = pRefKidT200->LookupObject();
- CPPUNIT_ASSERT(pObjectT200);
- auto pTypeT200 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT200->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT200->GetValue());
- auto pST200 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT200->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pST200->GetValue());
-
- auto pRefKidT3 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
- CPPUNIT_ASSERT(pRefKidT3);
- auto pObjectT3 = pRefKidT3->LookupObject();
- CPPUNIT_ASSERT(pObjectT3);
- auto pTypeT3 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT3->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT3->GetValue());
- auto pST3 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT3->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST3->GetValue());
-
- auto pKidsT3 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT3->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT3);
- auto pKidsT3v = pKidsT3->GetElements();
-
- auto pRefKidT30 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT3v[0]);
- CPPUNIT_ASSERT(pRefKidT30);
- auto pObjectT30 = pRefKidT30->LookupObject();
- CPPUNIT_ASSERT(pObjectT30);
- auto pTypeT30 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT30->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT30->GetValue());
- auto pST30 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT30->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST30->GetValue());
-
- auto pKidsT30 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT30->Lookup("K"));
- CPPUNIT_ASSERT(pKidsT30);
- auto pKidsT30v = pKidsT30->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT30v.size());
-
- // there is one and only one Link
- auto pRefKidT300 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT30v[0]);
- CPPUNIT_ASSERT(pRefKidT300);
- auto pObjectT300 = pRefKidT300->LookupObject();
- CPPUNIT_ASSERT(pObjectT300);
- auto pTypeT300 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT300->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT300->GetValue());
- auto pST300 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT300->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pST300->GetValue());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf135638)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"image-shape.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- int nFigure(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject)
- continue;
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- if (pType && pType->GetValue() == "StructElem")
- {
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
- if (pS && pS->GetValue() == "Figure")
- {
- auto pAttrDict
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject->Lookup("A"));
- CPPUNIT_ASSERT(pAttrDict != nullptr);
- auto pOwner
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAttrDict->LookupElement("O"));
- CPPUNIT_ASSERT(pOwner != nullptr);
- CPPUNIT_ASSERT_EQUAL(OString("Layout"), pOwner->GetValue());
- auto pBBox
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pAttrDict->LookupElement("BBox"));
- CPPUNIT_ASSERT(pBBox != nullptr);
- if (nFigure == 0)
- {
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 139.5,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[0])
- ->GetValue(),
- 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 480.3,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[1])
- ->GetValue(),
- 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 472.5,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[2])
- ->GetValue(),
- 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 735.3,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[3])
- ->GetValue(),
- 0.01);
- }
- else
- {
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 178.45,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[0])
- ->GetValue(),
- 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 318.65,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[1])
- ->GetValue(),
- 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 326.35,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[2])
- ->GetValue(),
- 0.01);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(
- 382.55,
- dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[3])
- ->GetValue(),
- 0.01);
- }
- ++nFigure;
- }
- }
- }
- // the first one is a Writer image, 2nd one SdrRectObj
- CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157703)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"LO_Lbl_Lbody_bug_report.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- vcl::filter::PDFObjectElement* pDocument(nullptr);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructElem")
- {
- auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
- if (pS1 && pS1->GetValue() == "Document")
- {
- pDocument = pObject1;
- }
- }
- }
- CPPUNIT_ASSERT(pDocument);
-
- auto pKidsD = dynamic_cast<vcl::filter::PDFArrayElement*>(pDocument->Lookup("K"));
- CPPUNIT_ASSERT(pKidsD);
- // assume there are no MCID ref at this level
- auto pKidsDv = pKidsD->GetElements();
- auto pRefKidD0 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsDv[0]);
- CPPUNIT_ASSERT(pRefKidD0);
- auto pObjectD0 = pRefKidD0->LookupObject();
- CPPUNIT_ASSERT(pObjectD0);
- auto pTypeD0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD0->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeD0->GetValue());
- auto pSD0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD0->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("H1"), pSD0->GetValue());
-
- auto pKidsD0 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectD0->Lookup("K"));
- CPPUNIT_ASSERT(pKidsD0);
- auto pKidsD0v = pKidsD0->GetElements();
- auto pRefKidD00 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD0v[0]);
- // MCID for label
- CPPUNIT_ASSERT(!pRefKidD00);
-
- // MCID for text
- auto pRefKidD01 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD0v[1]);
- CPPUNIT_ASSERT(!pRefKidD01);
-
- auto pRefKidD1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsDv[1]);
- CPPUNIT_ASSERT(pRefKidD1);
- auto pObjectD1 = pRefKidD1->LookupObject();
- CPPUNIT_ASSERT(pObjectD1);
- auto pTypeD1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD1->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeD1->GetValue());
- auto pSD1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD1->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("H2"), pSD1->GetValue());
-
- auto pKidsD1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectD1->Lookup("K"));
- CPPUNIT_ASSERT(pKidsD1);
- auto pKidsD1v = pKidsD1->GetElements();
-
- // MCID for text
- auto pRefKidD11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD1v[0]);
- CPPUNIT_ASSERT(!pRefKidD11);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testSpans)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"spanlist.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPages.size());
-
- auto nDoc(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructElem")
- {
- auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
- if (pS1 && pS1->GetValue() == "Document")
- {
- auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
- CPPUNIT_ASSERT(pKids1);
- // assume there are no MCID ref at this level
- auto vKids1 = pKids1->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(2), vKids1.size());
- auto pRefKid10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1[0]);
- CPPUNIT_ASSERT(pRefKid10);
- auto pObject10 = pRefKid10->LookupObject();
- CPPUNIT_ASSERT(pObject10);
- auto pType10
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10->GetValue());
- auto pS10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("L"), pS10->GetValue());
-
- auto pKids10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10->Lookup("K"));
- CPPUNIT_ASSERT(pKids10);
- // assume there are no MCID ref at this level
- auto vKids10 = pKids10->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(4), vKids10.size());
-
- auto pRefKid100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[0]);
- CPPUNIT_ASSERT(pRefKid100);
- auto pObject100 = pRefKid100->LookupObject();
- CPPUNIT_ASSERT(pObject100);
- auto pType100
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100->GetValue());
- auto pS100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LI"), pS100->GetValue());
-
- auto pKids100
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject100->Lookup("K"));
- CPPUNIT_ASSERT(pKids100);
- // assume there are no MCID ref at this level
- auto vKids100 = pKids100->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(2), vKids100.size());
-
- auto pRefKid1000 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids100[0]);
- CPPUNIT_ASSERT(pRefKid1000);
- auto pObject1000 = pRefKid1000->LookupObject();
- CPPUNIT_ASSERT(pObject1000);
- auto pType1000
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1000->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1000->GetValue());
- auto pS1000 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1000->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1000->GetValue());
-
- auto pRefKid1001 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids100[1]);
- CPPUNIT_ASSERT(pRefKid1001);
- auto pObject1001 = pRefKid1001->LookupObject();
- CPPUNIT_ASSERT(pObject1001);
- auto pType1001
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1001->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1001->GetValue());
- auto pS1001 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1001->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1001->GetValue());
- auto pKids1001
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1001->Lookup("K"));
- CPPUNIT_ASSERT(pKids1001);
- // assume there are no MCID ref at this level
- auto vKids1001 = pKids1001->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1001.size());
-
- auto pRefKid10010 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1001[0]);
- CPPUNIT_ASSERT(pRefKid10010);
- auto pObject10010 = pRefKid10010->LookupObject();
- CPPUNIT_ASSERT(pObject10010);
- auto pType10010
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10010->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10010->GetValue());
- auto pS10010
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10010->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10010->GetValue());
- auto pKids10010
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10010->Lookup("K"));
- CPPUNIT_ASSERT(pKids10010);
- // assume there are no MCID ref at this level
- auto vKids10010 = pKids10010->GetElements();
- // only one span
- CPPUNIT_ASSERT_EQUAL(size_t(1), vKids10010.size());
-
- auto pRefKid100100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10010[0]);
- CPPUNIT_ASSERT(pRefKid100100);
- auto pObject100100 = pRefKid100100->LookupObject();
- CPPUNIT_ASSERT(pObject100100);
- auto pType100100
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100100->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100100->GetValue());
- auto pS100100
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100100->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Span"), pS100100->GetValue());
- // this span exists because of lang
- auto pLang100100 = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
- pObject100100->Lookup("Lang"));
- CPPUNIT_ASSERT_EQUAL(OString("en-GB"), pLang100100->GetValue());
-
- auto pRefKid101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[1]);
- CPPUNIT_ASSERT(pRefKid101);
- auto pObject101 = pRefKid101->LookupObject();
- CPPUNIT_ASSERT(pObject101);
- auto pType101
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType101->GetValue());
- auto pS101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LI"), pS101->GetValue());
-
- auto pKids101
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject101->Lookup("K"));
- CPPUNIT_ASSERT(pKids101);
- // assume there are no MCID ref at this level
- auto vKids101 = pKids101->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(2), vKids101.size());
-
- auto pRefKid1010 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids101[0]);
- CPPUNIT_ASSERT(pRefKid1010);
- auto pObject1010 = pRefKid1010->LookupObject();
- CPPUNIT_ASSERT(pObject1010);
- auto pType1010
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1010->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1010->GetValue());
- auto pS1010 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1010->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1010->GetValue());
-
- auto pRefKid1011 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids101[1]);
- CPPUNIT_ASSERT(pRefKid1011);
- auto pObject1011 = pRefKid1011->LookupObject();
- CPPUNIT_ASSERT(pObject1011);
- auto pType1011
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1011->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1011->GetValue());
- auto pS1011 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1011->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1011->GetValue());
-
- auto pKids1011
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1011->Lookup("K"));
- CPPUNIT_ASSERT(pKids1011);
- // assume there are no MCID ref at this level
- auto vKids1011 = pKids1011->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1011.size());
-
- auto pRefKid10110 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1011[0]);
- CPPUNIT_ASSERT(pRefKid10110);
- auto pObject10110 = pRefKid10110->LookupObject();
- CPPUNIT_ASSERT(pObject10110);
- auto pType10110
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10110->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10110->GetValue());
- auto pS10110
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10110->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10110->GetValue());
- auto pKids10110
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10110->Lookup("K"));
- CPPUNIT_ASSERT(pKids10110);
- auto vKids10110 = pKids10110->GetElements();
- // only MCIDs, no span
- for (size_t i = 0; i < vKids10110.size(); ++i)
- {
- auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10110[i]);
- CPPUNIT_ASSERT(!pKid);
- }
-
- auto pRefKid102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[2]);
- CPPUNIT_ASSERT(pRefKid102);
- auto pObject102 = pRefKid102->LookupObject();
- CPPUNIT_ASSERT(pObject102);
- auto pType102
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102->GetValue());
- auto pS102 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LI"), pS102->GetValue());
-
- auto pKids102
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102->Lookup("K"));
- CPPUNIT_ASSERT(pKids102);
- // assume there are no MCID ref at this level
- auto vKids102 = pKids102->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(2), vKids102.size());
-
- auto pRefKid1020 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102[0]);
- CPPUNIT_ASSERT(pRefKid1020);
- auto pObject1020 = pRefKid1020->LookupObject();
- CPPUNIT_ASSERT(pObject1020);
- auto pType1020
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1020->GetValue());
- auto pS1020 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1020->GetValue());
-
- auto pRefKid1021 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102[1]);
- CPPUNIT_ASSERT(pRefKid1021);
- auto pObject1021 = pRefKid1021->LookupObject();
- CPPUNIT_ASSERT(pObject1021);
- auto pType1021
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1021->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1021->GetValue());
- auto pS1021 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1021->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1021->GetValue());
-
- auto pKids1021
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1021->Lookup("K"));
- CPPUNIT_ASSERT(pKids1021);
- // assume there are no MCID ref at this level
- auto vKids1021 = pKids1021->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1021.size());
-
- auto pRefKid10210 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1021[0]);
- CPPUNIT_ASSERT(pRefKid10210);
- auto pObject10210 = pRefKid10210->LookupObject();
- CPPUNIT_ASSERT(pObject10210);
- auto pType10210
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10210->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10210->GetValue());
- auto pS10210
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10210->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10210->GetValue());
- auto pKids10210
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10210->Lookup("K"));
- CPPUNIT_ASSERT(pKids10210);
- // assume there are no MCID ref at this level
- auto vKids10210 = pKids10210->GetElements();
- // 2 span and a hyperlink
- CPPUNIT_ASSERT_EQUAL(size_t(3), vKids10210.size());
-
- auto pRefKid102100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[0]);
- CPPUNIT_ASSERT(pRefKid102100);
- auto pObject102100 = pRefKid102100->LookupObject();
- CPPUNIT_ASSERT(pObject102100);
- auto pType102100
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102100->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102100->GetValue());
- auto pS102100
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102100->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Span"), pS102100->GetValue());
- auto pKids102100
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102100->Lookup("K"));
- CPPUNIT_ASSERT(pKids102100);
- auto vKids102100 = pKids102100->GetElements();
- for (size_t i = 0; i < vKids102100.size(); ++i)
- {
- auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102100[i]);
- CPPUNIT_ASSERT(!pKid);
- }
-
- auto pRefKid102101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[1]);
- CPPUNIT_ASSERT(pRefKid102101);
- auto pObject102101 = pRefKid102101->LookupObject();
- CPPUNIT_ASSERT(pObject102101);
- auto pType102101
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102101->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102101->GetValue());
- auto pS102101
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102101->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pS102101->GetValue());
- auto pKids102101
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102101->Lookup("K"));
- CPPUNIT_ASSERT(pKids102101);
- auto vKids102101 = pKids102101->GetElements();
- auto nRef(0);
- for (size_t i = 0; i < vKids102101.size(); ++i)
- {
- auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102101[i]);
- if (pKid)
- {
- ++nRef; // annotation
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
-
- auto pRefKid102102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[2]);
- CPPUNIT_ASSERT(pRefKid102102);
- auto pObject102102 = pRefKid102102->LookupObject();
- CPPUNIT_ASSERT(pObject102102);
- auto pType102102
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102102->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102102->GetValue());
- auto pS102102
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102102->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Span"), pS102102->GetValue());
- auto pKids102102
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102102->Lookup("K"));
- CPPUNIT_ASSERT(pKids102102);
- auto vKids102102 = pKids102102->GetElements();
- // there is a footnote
- auto nFtn(0);
- for (size_t i = 0; i < vKids102102.size(); ++i)
- {
- auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102102[i]);
- if (pKid)
- {
- auto pObject = pKid->LookupObject();
- CPPUNIT_ASSERT(pObject);
- auto pType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pS->GetValue());
- ++nFtn;
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFtn)>(1), nFtn);
-
- auto pRefKid103 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[3]);
- CPPUNIT_ASSERT(pRefKid103);
- auto pObject103 = pRefKid103->LookupObject();
- CPPUNIT_ASSERT(pObject103);
- auto pType103
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103->GetValue());
- auto pS103 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LI"), pS103->GetValue());
-
- auto pKids103
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject103->Lookup("K"));
- CPPUNIT_ASSERT(pKids103);
- // assume there are no MCID ref at this level
- auto vKids103 = pKids103->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(2), vKids103.size());
-
- auto pRefKid1030 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids103[0]);
- CPPUNIT_ASSERT(pRefKid1030);
- auto pObject1030 = pRefKid1030->LookupObject();
- CPPUNIT_ASSERT(pObject1030);
- auto pType1030
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1030->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1030->GetValue());
- auto pS1030 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1030->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1030->GetValue());
-
- auto pRefKid1031 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids103[1]);
- CPPUNIT_ASSERT(pRefKid1031);
- auto pObject1031 = pRefKid1031->LookupObject();
- CPPUNIT_ASSERT(pObject1031);
- auto pType1031
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1031->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1031->GetValue());
- auto pS1031 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1031->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1031->GetValue());
-
- auto pKids1031
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1031->Lookup("K"));
- CPPUNIT_ASSERT(pKids1031);
- // assume there are no MCID ref at this level
- auto vKids1031 = pKids1031->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1031.size());
-
- auto pRefKid10310 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1031[0]);
- CPPUNIT_ASSERT(pRefKid10310);
- auto pObject10310 = pRefKid10310->LookupObject();
- CPPUNIT_ASSERT(pObject10310);
- auto pType10310
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10310->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10310->GetValue());
- auto pS10310
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10310->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10310->GetValue());
- auto pKids10310
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10310->Lookup("K"));
- CPPUNIT_ASSERT(pKids10310);
- // assume there are no MCID ref at this level
- auto vKids10310 = pKids10310->GetElements();
- // only one span, following a MCID for some strike-out gap
- CPPUNIT_ASSERT_EQUAL(size_t(2), vKids10310.size());
-
- auto pRefKid103100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10310[0]);
- CPPUNIT_ASSERT(!pRefKid103100);
-
- auto pRefKid103101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10310[1]);
- CPPUNIT_ASSERT(pRefKid103101);
- auto pObject103101 = pRefKid103101->LookupObject();
- CPPUNIT_ASSERT(pObject103101);
- auto pType103101
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103101->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103101->GetValue());
- auto pS103101
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103101->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Span"), pS103101->GetValue());
- auto pDictA103101
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject103101->Lookup("A"));
- CPPUNIT_ASSERT(pDictA103101 != nullptr);
- CPPUNIT_ASSERT_EQUAL(OString("Layout"), dynamic_cast<vcl::filter::PDFNameElement*>(
- pDictA103101->LookupElement("O"))
- ->GetValue());
- CPPUNIT_ASSERT_EQUAL(OString("LineThrough"),
- dynamic_cast<vcl::filter::PDFNameElement*>(
- pDictA103101->LookupElement("TextDecorationType"))
- ->GetValue());
-
- // now the footnote container - following the list
- auto pRefKid11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1[1]);
- CPPUNIT_ASSERT(pRefKid11);
- auto pObject11 = pRefKid11->LookupObject();
- CPPUNIT_ASSERT(pObject11);
- auto pType11
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11->GetValue());
- auto pS11 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Div"), pS11->GetValue());
-
- auto pKids11 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject11->Lookup("K"));
- CPPUNIT_ASSERT(pKids11);
- // assume there are no MCID ref at this level
- auto vKids11 = pKids11->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), vKids11.size());
-
- auto pRefKid110 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids11[0]);
- CPPUNIT_ASSERT(pRefKid110);
- auto pObject110 = pRefKid110->LookupObject();
- CPPUNIT_ASSERT(pObject110);
- auto pType110
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject110->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType110->GetValue());
- auto pS110 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject110->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Note"), pS110->GetValue());
-
- auto pKids110
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject110->Lookup("K"));
- CPPUNIT_ASSERT(pKids110);
- // assume there are no MCID ref at this level
- auto vKids110 = pKids110->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(2), vKids110.size());
-
- auto pRefKid1100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids110[0]);
- CPPUNIT_ASSERT(pRefKid1100);
- auto pObject1100 = pRefKid1100->LookupObject();
- CPPUNIT_ASSERT(pObject1100);
- auto pType1100
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1100->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1100->GetValue());
- auto pS1100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1100->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1100->GetValue());
-
- auto pKids1100
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1100->Lookup("K"));
- CPPUNIT_ASSERT(pKids1100);
- // assume there are no MCID ref at this level
- auto vKids1100 = pKids1100->GetElements();
- CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1100.size());
-
- auto pRefKid11000 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1100[0]);
- CPPUNIT_ASSERT(pRefKid11000);
- auto pObject11000 = pRefKid11000->LookupObject();
- CPPUNIT_ASSERT(pObject11000);
- auto pType11000
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11000->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11000->GetValue());
- auto pS11000
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11000->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pS11000->GetValue());
-
- auto pRefKid1101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids110[1]);
- CPPUNIT_ASSERT(pRefKid1101);
- auto pObject1101 = pRefKid1101->LookupObject();
- CPPUNIT_ASSERT(pObject1101);
- auto pType1101
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1101->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1101->GetValue());
- auto pS1101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1101->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Footnote"), pS1101->GetValue());
-
- ++nDoc;
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDoc)>(1), nDoc);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157182)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({
- { "PDFUACompliance", uno::Any(true) },
- // only happens with PDF/A-1
- { "SelectPdfVersion", uno::Any(static_cast<sal_Int32>(1)) },
- }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
-
- saveAsPDF(u"transparentshape.fodp");
-
- // just check this does not crash or assert
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf57423)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"Description PDF Export test .odt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- int nFigure(0);
- int nFormula(0);
- int nDiv(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject)
- continue;
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- if (pType && pType->GetValue() == "StructElem")
- {
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
- if (pS && pS->GetValue() == "Figure")
- {
- switch (nFigure)
- {
- case 2:
- CPPUNIT_ASSERT_EQUAL(u"QR Code - Tells how to get to Mosegaard"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- case 0:
- CPPUNIT_ASSERT_EQUAL(u"Title: Arrows - Description: Explains the "
- u"different arrow appearances"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- case 1:
- CPPUNIT_ASSERT_EQUAL(
- u"My blue triangle - Does not need further description"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- }
- ++nFigure;
- }
- if (pS && pS->GetValue() == "Formula")
- {
- CPPUNIT_ASSERT_EQUAL(
- u"Equation 1 - Now we give the full description of eq 1 here"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(pObject->Lookup("Alt"))));
- ++nFormula;
- }
- if (pS && pS->GetValue() == "Div")
- {
- switch (nDiv)
- {
- case 0:
- CPPUNIT_ASSERT_EQUAL(u"This frame has a description"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- case 1:
- // no properties set on this
- CPPUNIT_ASSERT(!pObject->Lookup("Alt"));
- break;
- case 2:
- CPPUNIT_ASSERT_EQUAL(u"My textbox - Has a light background"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- case 3:
- CPPUNIT_ASSERT_EQUAL(u"Hey! There is no alternate text for Frame "
- u"// but maybe not needed?"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- }
- ++nDiv;
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(3), nFigure);
- CPPUNIT_ASSERT_EQUAL(int(1), nFormula);
- CPPUNIT_ASSERT_EQUAL(int(4), nDiv);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf154982)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"tdf154982.odt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- int nFigure(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject)
- continue;
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- if (pType && pType->GetValue() == "StructElem")
- {
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
- if (pS && pS->GetValue() == "Figure")
- {
- switch (nFigure)
- {
- case 0:
- CPPUNIT_ASSERT_EQUAL(u"Here comes the signature - Please sign here"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- case 1:
- CPPUNIT_ASSERT_EQUAL(u"Home"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- }
-
- // the problem was that the figures in the hell layer were not
- // below their anchor paragraphs in the structure tree
- auto pParentRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject->Lookup("P"));
- CPPUNIT_ASSERT(pParentRef);
- auto pParent(pParentRef->LookupObject());
- CPPUNIT_ASSERT(pParent);
- auto pParentType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
- auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pParentS->GetValue());
-
- auto pPParentRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pParent->Lookup("P"));
- CPPUNIT_ASSERT(pPParentRef);
- auto pPParent(pPParentRef->LookupObject());
- CPPUNIT_ASSERT(pPParent);
- auto pPParentType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pPParentType->GetValue());
- auto pPParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Document"), pPParentS->GetValue());
- ++nFigure;
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf157397)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"PDF_export_with_formcontrol.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- vcl::filter::PDFObjectElement* pDocument(nullptr);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructElem")
- {
- auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
- if (pS1 && pS1->GetValue() == "Document")
- {
- pDocument = pObject1;
- }
- }
- }
- CPPUNIT_ASSERT(pDocument);
-
- auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pDocument->Lookup("K"));
- CPPUNIT_ASSERT(pKids1);
- // assume there are no MCID ref at this level
- auto pKids1v = pKids1->GetElements();
- auto pRefKid12 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[2]);
- CPPUNIT_ASSERT(pRefKid12);
- auto pObject12 = pRefKid12->LookupObject();
- CPPUNIT_ASSERT(pObject12);
- auto pType12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType12->GetValue());
- auto pS12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS12->GetValue());
-
- auto pKids12 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject12->Lookup("K"));
- CPPUNIT_ASSERT(pKids12);
- // assume there are no MCID ref at this level
- auto pKids12v = pKids12->GetElements();
- auto pRefKid120 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids12v[0]);
- CPPUNIT_ASSERT(pRefKid120);
- auto pObject120 = pRefKid120->LookupObject();
- CPPUNIT_ASSERT(pObject120);
- auto pType120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType120->GetValue());
- auto pS120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pS120->GetValue());
-
- {
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject120->Lookup("K"));
- auto nMCID(0);
- auto nRef(0);
- for (size_t i = 0; i < pKids->GetElements().size(); ++i)
- {
- auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
- if (pNum)
- {
- ++nMCID;
- }
- if (pRef)
- {
- ++nRef;
- auto pObjR = pRef->LookupObject();
- auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
- auto pAnnotRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
- auto pAnnot = pAnnotRef->LookupObject();
- auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
- auto pASubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
- auto pAContents
- = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
- CPPUNIT_ASSERT_EQUAL(
- u"https://klexikon.zum.de/wiki/Kläranlage"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
- auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
- CPPUNIT_ASSERT(pAA);
- auto pAAType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
- auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
- CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
- auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
- pAA->LookupElement("URI"));
- CPPUNIT_ASSERT_EQUAL(OString("https://klexikon.zum.de/wiki/Kl%C3%A4ranlage"),
- pAAURI->GetValue());
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
- }
-
- auto pRefKid13 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[3]);
- CPPUNIT_ASSERT(pRefKid13);
- auto pObject13 = pRefKid13->LookupObject();
- CPPUNIT_ASSERT(pObject13);
- auto pType13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType13->GetValue());
- auto pS13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS13->GetValue());
-
- auto pKids13 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject13->Lookup("K"));
- CPPUNIT_ASSERT(pKids13);
- // assume there are no MCID ref at this level
- auto pKids13v = pKids13->GetElements();
- auto pRefKid130 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids13v[0]);
- CPPUNIT_ASSERT(pRefKid130);
- auto pObject130 = pRefKid130->LookupObject();
- CPPUNIT_ASSERT(pObject130);
- auto pType130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType130->GetValue());
- auto pS130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pS130->GetValue());
-
- {
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject130->Lookup("K"));
- auto nMCID(0);
- auto nRef(0);
- for (size_t i = 0; i < pKids->GetElements().size(); ++i)
- {
- auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
- if (pNum)
- {
- ++nMCID;
- }
- if (pRef)
- {
- ++nRef;
- auto pObjR = pRef->LookupObject();
- auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
- auto pAnnotRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
- auto pAnnot = pAnnotRef->LookupObject();
- auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
- auto pASubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
- auto pAContents
- = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
- CPPUNIT_ASSERT_EQUAL(
- u"https://de.wikipedia.org/wiki/Kläranlage#Mechanische_Vorreinigung"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
- auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
- CPPUNIT_ASSERT(pAA);
- auto pAAType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
- auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
- CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
- auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
- pAA->LookupElement("URI"));
- CPPUNIT_ASSERT_EQUAL(
- OString(
- "https://de.wikipedia.org/wiki/Kl%C3%A4ranlage#Mechanische_Vorreinigung"),
- pAAURI->GetValue());
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
- }
-
- auto pRefKid14 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[4]);
- CPPUNIT_ASSERT(pRefKid14);
- auto pObject14 = pRefKid14->LookupObject();
- CPPUNIT_ASSERT(pObject14);
- auto pType14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType14->GetValue());
- auto pS14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS14->GetValue());
-
- auto pKids14 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject14->Lookup("K"));
- CPPUNIT_ASSERT(pKids14);
- // assume there are no MCID ref at this level
- auto pKids14v = pKids14->GetElements();
- auto pRefKid140 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids14v[0]);
- CPPUNIT_ASSERT(pRefKid140);
- auto pObject140 = pRefKid140->LookupObject();
- CPPUNIT_ASSERT(pObject140);
- auto pType140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType140->GetValue());
- auto pS140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pS140->GetValue());
-
- {
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject140->Lookup("K"));
- auto nMCID(0);
- auto nRef(0);
- for (size_t i = 0; i < pKids->GetElements().size(); ++i)
- {
- auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
- if (pNum)
- {
- ++nMCID;
- }
- if (pRef)
- {
- ++nRef;
- auto pObjR = pRef->LookupObject();
- auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
- auto pAnnotRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
- auto pAnnot = pAnnotRef->LookupObject();
- auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
- auto pASubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
- auto pAContents
- = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
- CPPUNIT_ASSERT_EQUAL(
- u"https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
- auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
- CPPUNIT_ASSERT(pAA);
- auto pAAType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
- auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
- CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
- auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
- pAA->LookupElement("URI"));
- CPPUNIT_ASSERT_EQUAL(
- OString("https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"),
- pAAURI->GetValue());
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
- }
-
- auto pRefKid16 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[6]);
- CPPUNIT_ASSERT(pRefKid16);
- auto pObject16 = pRefKid16->LookupObject();
- CPPUNIT_ASSERT(pObject16);
- auto pType16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType16->GetValue());
- auto pS16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS16->GetValue());
-
- auto pKids16 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject16->Lookup("K"));
- CPPUNIT_ASSERT(pKids16);
- // assume there are no MCID ref at this level
- auto pKids16v = pKids16->GetElements();
- auto pRefKid160 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids16v[0]);
- CPPUNIT_ASSERT(pRefKid160);
- auto pObject160 = pRefKid160->LookupObject();
- CPPUNIT_ASSERT(pObject160);
- auto pType160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType160->GetValue());
- auto pS160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pS160->GetValue());
- auto pA160Dict = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject160->Lookup("A"));
- CPPUNIT_ASSERT(pA160Dict);
- auto pA160O = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Dict->LookupElement("O"));
- CPPUNIT_ASSERT(pA160O);
- CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pA160O->GetValue());
- auto pA160Role = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Dict->LookupElement("Role"));
- CPPUNIT_ASSERT(pA160Role);
- CPPUNIT_ASSERT_EQUAL(OString("tv"), pA160Role->GetValue());
-
- {
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject160->Lookup("K"));
- auto nMCID(0);
- auto nRef(0);
- for (size_t i = 0; i < pKids->GetElements().size(); ++i)
- {
- auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
- if (pNum)
- {
- ++nMCID;
- }
- if (pRef)
- {
- ++nRef;
- auto pObjR = pRef->LookupObject();
- auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
- auto pAnnotRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
- auto pAnnot = pAnnotRef->LookupObject();
- auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
- auto pASubtype
- = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
- CPPUNIT_ASSERT_EQUAL(OString("Widget"), pASubtype->GetValue());
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf135192)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"tdf135192-1.fodp");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- int nTable(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructElem")
- {
- auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
- if (pS1 && pS1->GetValue() == "Table")
- {
- int nTR(0);
- auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
- CPPUNIT_ASSERT(pKids1);
- // there can be additional children, such as MCID ref
- for (auto pKid1 : pKids1->GetElements())
- {
- auto pRefKid1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid1);
- if (pRefKid1)
- {
- auto pObject2 = pRefKid1->LookupObject();
- if (pObject2)
- {
- auto pType2 = dynamic_cast<vcl::filter::PDFNameElement*>(
- pObject2->Lookup("Type"));
- if (pType2 && pType2->GetValue() == "StructElem")
- {
- auto pS2 = dynamic_cast<vcl::filter::PDFNameElement*>(
- pObject2->Lookup("S"));
- if (pS2 && pS2->GetValue() == "TR")
- {
- int nTD(0);
- auto pKids2 = dynamic_cast<vcl::filter::PDFArrayElement*>(
- pObject2->Lookup("K"));
- CPPUNIT_ASSERT(pKids2);
- for (auto pKid2 : pKids2->GetElements())
- {
- auto pRefKid2
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(
- pKid2);
- if (pRefKid2)
- {
- auto pObject3 = pRefKid2->LookupObject();
- if (pObject3)
- {
- auto pType3
- = dynamic_cast<vcl::filter::PDFNameElement*>(
- pObject3->Lookup("Type"));
- if (pType3 && pType3->GetValue() == "StructElem")
- {
- auto pS3 = dynamic_cast<
- vcl::filter::PDFNameElement*>(
- pObject3->Lookup("S"));
- if (nTR == 0 && pS3 && pS3->GetValue() == "TH")
- {
- int nOTable(0);
- auto pAttrs = dynamic_cast<
- vcl::filter::PDFArrayElement*>(
- pObject3->Lookup("A"));
- CPPUNIT_ASSERT(pAttrs != nullptr);
- for (const auto& rAttrRef :
- pAttrs->GetElements())
- {
- auto pAttrDict = dynamic_cast<
- vcl::filter::PDFDictionaryElement*>(
- rAttrRef);
- CPPUNIT_ASSERT(pAttrDict != nullptr);
- auto pOwner = dynamic_cast<
- vcl::filter::PDFNameElement*>(
- pAttrDict->LookupElement("O"));
- CPPUNIT_ASSERT(pOwner != nullptr);
- if (pOwner->GetValue() == "Table")
- {
- auto pScope = dynamic_cast<
- vcl::filter::PDFNameElement*>(
- pAttrDict->LookupElement(
- "Scope"));
- CPPUNIT_ASSERT(pScope != nullptr);
- CPPUNIT_ASSERT_EQUAL(
- OString("Column"),
- pScope->GetValue());
- ++nOTable;
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(1), nOTable);
- ++nTD;
- }
- else if (nTR != 0 && pS3
- && pS3->GetValue() == "TD")
- {
- ++nTD;
- }
- }
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(3), nTD);
- ++nTR;
- }
- }
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(2), nTR);
- ++nTable;
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(int(1), nTable);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf154955)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- saveAsPDF(u"grouped-shape.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
- CPPUNIT_ASSERT(pContents);
- vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- // Uncompress it.
- SvMemoryStream aUncompressed;
- ZCodec aZCodec;
- aZCodec.BeginCompression();
- rObjectStream.Seek(0);
- aZCodec.Decompress(rObjectStream, aUncompressed);
- CPPUNIT_ASSERT(aZCodec.EndCompression());
-
- auto pStart = static_cast<const char*>(aUncompressed.GetData());
- const char* const pEnd = pStart + aUncompressed.GetSize();
-
- enum
- {
- Default,
- Artifact,
- Tagged
- } state
- = Default;
-
- auto nLine(0);
- auto nTagged(0);
- auto nArtifacts(0);
- while (true)
- {
- ++nLine;
- auto const pLine = ::std::find(pStart, pEnd, '\n');
- if (pLine == pEnd)
- {
- break;
- }
- std::string_view const line(pStart, pLine - pStart);
- pStart = pLine + 1;
- if (!line.empty() && line[0] != '%')
- {
- ::std::cerr << nLine << ": " << line << "\n";
- if (o3tl::starts_with(line, "/Artifact "))
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Artifact;
- ++nArtifacts;
- }
- else if (o3tl::starts_with(line, "/Figure<</MCID "))
- {
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
- state = Tagged;
- ++nTagged;
- }
- else if (line == "EMC")
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
- state = Default;
- }
- else if (nLine > 1) // first line is expected "0.1 w"
- {
- CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged)>(2), nTagged);
- CPPUNIT_ASSERT(nArtifacts >= 1);
-
- int nFigure(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject)
- continue;
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- if (pType && pType->GetValue() == "StructElem")
- {
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
- if (pS && pS->GetValue() == "Figure")
- {
- switch (nFigure)
- {
- case 0:
- CPPUNIT_ASSERT_EQUAL(u"Two rectangles - Grouped"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- case 1:
- CPPUNIT_ASSERT_EQUAL(u"these ones are green"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject->Lookup("Alt"))));
- break;
- }
-
- auto pParentRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject->Lookup("P"));
- CPPUNIT_ASSERT(pParentRef);
- auto pParent(pParentRef->LookupObject());
- CPPUNIT_ASSERT(pParent);
- auto pParentType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
- auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Standard"), pParentS->GetValue());
-
- ++nFigure;
- }
- }
- }
- // the problem was that there were 4 shapes (the sub-shapes of the 2 groups)
- CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf155190)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
-
- saveAsPDF(u"tdf155190.odt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- auto nDiv(0);
- auto nFigure(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
-
- auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
- // start with the text box
- if (pType1 && pType1->GetValue() == "StructElem" && pS1 && pS1->GetValue() == "Div")
- {
- ++nDiv;
- auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
- CPPUNIT_ASSERT(pKids1);
- for (auto pKid1 : pKids1->GetElements())
- {
- auto pRefKid1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid1);
- if (pRefKid1)
- {
- auto pObject2 = pRefKid1->LookupObject();
- CPPUNIT_ASSERT(pObject2);
- auto pType2
- = dynamic_cast<vcl::filter::PDFNameElement*>(pObject2->Lookup("Type"));
- CPPUNIT_ASSERT(pType2);
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType2->GetValue());
- auto pS2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject2->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("FigureCaption"), pS2->GetValue());
- auto pKids2
- = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject2->Lookup("K"));
- CPPUNIT_ASSERT(pKids2);
- // there are additional children, MCID ref
- for (auto pKid2 : pKids2->GetElements())
- {
- auto pRefKid2 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid2);
- if (pRefKid2)
- {
- auto pObject3 = pRefKid2->LookupObject();
- CPPUNIT_ASSERT(pObject3);
- auto pType3 = dynamic_cast<vcl::filter::PDFNameElement*>(
- pObject3->Lookup("Type"));
- if (pType3 && pType3->GetValue() == "StructElem")
- {
- auto pS3 = dynamic_cast<vcl::filter::PDFNameElement*>(
- pObject3->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Figure"), pS3->GetValue());
- auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pObject3->Lookup("Alt"));
- CPPUNIT_ASSERT_EQUAL(
- OUString("Picture of apples"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
- auto pKids3 = dynamic_cast<vcl::filter::PDFArrayElement*>(
- pObject3->Lookup("K"));
- CPPUNIT_ASSERT(pKids3);
- // the problem was that this didn't reference an MCID
- CPPUNIT_ASSERT(!pKids3->GetElements().empty());
- ++nFigure;
- }
- }
- }
- }
- }
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDiv)>(1), nDiv);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDiv)>(1), nFigure);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testMediaShapeAnnot)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
-
- saveAsPDF(u"vid.odt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
- CPPUNIT_ASSERT(pAnnots);
-
- // There should be one annotation
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
- auto pAnnotReference
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
- CPPUNIT_ASSERT(pAnnotReference);
- // check /Annot - produced by sw
- vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
- CPPUNIT_ASSERT(pAnnot);
- CPPUNIT_ASSERT_EQUAL(
- OString("Annot"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- OString("Screen"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
-
- auto pA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
- CPPUNIT_ASSERT(pA);
- auto pR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pA->LookupElement("R"));
- CPPUNIT_ASSERT(pR);
- auto pC = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pR->LookupElement("C"));
- CPPUNIT_ASSERT(pC);
- auto pCT = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pC->LookupElement("CT"));
- CPPUNIT_ASSERT_EQUAL(OString("video/webm"), pCT->GetValue());
- auto pD = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pC->LookupElement("D"));
- CPPUNIT_ASSERT(pD);
- auto pDesc = dynamic_cast<vcl::filter::PDFHexStringElement*>(pD->LookupElement("Desc"));
- CPPUNIT_ASSERT(pDesc);
- CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text\nand some description"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pDesc));
- auto pAlt = dynamic_cast<vcl::filter::PDFArrayElement*>(pC->LookupElement("Alt"));
- CPPUNIT_ASSERT(pAlt);
- auto pLang = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAlt->GetElement(0));
- CPPUNIT_ASSERT_EQUAL(OString(""), pLang->GetValue());
- auto pAltText = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAlt->GetElement(1));
- CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text\nand some description"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAltText));
-
- auto pStructParent
- = dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
- CPPUNIT_ASSERT(pStructParent);
-
- vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
-
- // check ParentTree to find StructElem
- auto nRoots(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructTreeRoot")
- {
- ++nRoots;
- auto pParentTree
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
- CPPUNIT_ASSERT(pParentTree);
- auto pNumTree = pParentTree->LookupObject();
- CPPUNIT_ASSERT(pNumTree);
- auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
- CPPUNIT_ASSERT(pNums);
- auto nFound(0);
- for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
- {
- auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
- if (pI->GetValue() == pStructParent->GetValue())
- {
- ++nFound;
- CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
- pStructElemRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
- CPPUNIT_ASSERT(pStructElemRef);
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
-
- // check /StructElem - produced by drawinglayer
- CPPUNIT_ASSERT(pStructElemRef);
- auto pStructElem(pStructElemRef->LookupObject());
- CPPUNIT_ASSERT(pStructElem);
-
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Annot"), pS->GetValue());
- auto pSEAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
- CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text - and some description"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pSEAlt));
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
- auto nMCID(0);
- auto nRef(0);
- for (size_t i = 0; i < pKids->GetElements().size(); ++i)
- {
- auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
- if (pNum)
- {
- ++nMCID;
- }
- if (pRef)
- {
- ++nRef;
- auto pObjR = pRef->LookupObject();
- auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
- auto pAnnotRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
- CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testFlyFrameHyperlinkAnnot)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
-
- saveAsPDF(u"image-hyperlink-alttext.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
- CPPUNIT_ASSERT(pAnnots);
-
- // There should be one annotation
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
- auto pAnnotReference
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
- CPPUNIT_ASSERT(pAnnotReference);
- // check /Annot - produced by sw
- vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
- CPPUNIT_ASSERT(pAnnot);
- CPPUNIT_ASSERT_EQUAL(
- OString("Annot"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- OString("Link"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
-
- auto pContents = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
- CPPUNIT_ASSERT_EQUAL(OUString("Image2"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pContents));
-
- auto pStructParent
- = dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
- CPPUNIT_ASSERT(pStructParent);
-
- vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
-
- // check ParentTree to find StructElem
- auto nRoots(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructTreeRoot")
- {
- ++nRoots;
- auto pParentTree
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
- CPPUNIT_ASSERT(pParentTree);
- auto pNumTree = pParentTree->LookupObject();
- CPPUNIT_ASSERT(pNumTree);
- auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
- CPPUNIT_ASSERT(pNums);
- auto nFound(0);
- for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
- {
- auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
- if (pI->GetValue() == pStructParent->GetValue())
- {
- ++nFound;
- CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
- pStructElemRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
- CPPUNIT_ASSERT(pStructElemRef);
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
-
- // check /StructElem - produced by sw painting code
- CPPUNIT_ASSERT(pStructElemRef);
- auto pStructElem(pStructElemRef->LookupObject());
- CPPUNIT_ASSERT(pStructElem);
-
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Link"), pS->GetValue());
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
- auto nMCID(0);
- auto nRef(0);
- for (size_t i = 0; i < pKids->GetElements().size(); ++i)
- {
- auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
- if (pNum)
- {
- ++nMCID;
- }
- if (pRef)
- {
- ++nRef;
- auto pObjR = pRef->LookupObject();
- auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
- auto pAnnotRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
- CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
-
- // the Link is inside a Figure
- auto pParentRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pStructElem->Lookup("P"));
- CPPUNIT_ASSERT(pParentRef);
- auto pParent(pParentRef->LookupObject());
- CPPUNIT_ASSERT(pParent);
- auto pParentType = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
- auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Figure"), pParentS->GetValue());
- auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pParent->Lookup("Alt"));
- CPPUNIT_ASSERT_EQUAL(OUString("Ship drawing - Very cute"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testFormControlAnnot)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable PDF/UA
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
-
- saveAsPDF(u"formcontrol.fodt");
-
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
- CPPUNIT_ASSERT(pAnnots);
-
- // There should be one annotation
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
- auto pAnnotReference
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
- CPPUNIT_ASSERT(pAnnotReference);
- // check /Annot
- vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
- CPPUNIT_ASSERT(pAnnot);
- CPPUNIT_ASSERT_EQUAL(
- OString("Annot"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- OString("Widget"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
- auto pT = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAnnot->Lookup("T"));
- CPPUNIT_ASSERT(pT);
- CPPUNIT_ASSERT_EQUAL(OString("Check Box 1"), pT->GetValue());
- auto pTU = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("TU"));
- CPPUNIT_ASSERT(pTU);
- CPPUNIT_ASSERT_EQUAL(OUString("helpful text"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pTU));
-
- auto pStructParent
- = dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
- CPPUNIT_ASSERT(pStructParent);
-
- vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
-
- // check ParentTree to find StructElem
- auto nRoots(0);
- for (const auto& rDocElement : aDocument.GetElements())
- {
- auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
- if (!pObject1)
- continue;
- auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
- if (pType1 && pType1->GetValue() == "StructTreeRoot")
- {
- ++nRoots;
- auto pParentTree
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
- CPPUNIT_ASSERT(pParentTree);
- auto pNumTree = pParentTree->LookupObject();
- CPPUNIT_ASSERT(pNumTree);
- auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
- CPPUNIT_ASSERT(pNums);
- auto nFound(0);
- for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
- {
- auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
- if (pI->GetValue() == pStructParent->GetValue())
- {
- ++nFound;
- CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
- pStructElemRef
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
- CPPUNIT_ASSERT(pStructElemRef);
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
-
- // check /StructElem
- CPPUNIT_ASSERT(pStructElemRef);
- auto pStructElem(pStructElemRef->LookupObject());
- CPPUNIT_ASSERT(pStructElem);
-
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
- auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
- CPPUNIT_ASSERT_EQUAL(OString("Form"), pS->GetValue());
- auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
- CPPUNIT_ASSERT_EQUAL(OUString("textuelle alternative - a box to check"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
- auto pA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pStructElem->Lookup("A"));
- CPPUNIT_ASSERT(pA);
- auto pO = dynamic_cast<vcl::filter::PDFNameElement*>(pA->LookupElement("O"));
- CPPUNIT_ASSERT(pO);
- CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pO->GetValue());
- auto pRole = dynamic_cast<vcl::filter::PDFNameElement*>(pA->LookupElement("Role"));
- CPPUNIT_ASSERT(pRole);
- CPPUNIT_ASSERT_EQUAL(OString("cb"), pRole->GetValue());
- auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
- auto nMCID(0);
- auto nRef(0);
- for (size_t i = 0; i < pKids->GetElements().size(); ++i)
- {
- auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
- auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
- if (pNum)
- {
- ++nMCID;
- }
- if (pRef)
- {
- ++nRef;
- auto pObjR = pRef->LookupObject();
- auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
- CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
- auto pAnnotRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
- CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
- }
- }
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
- CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf142129)
-{
- loadFromURL(u"master.odm");
-
- // update linked section
- dispatchCommand(mxComponent, ".uno:UpdateAllLinks", {});
-
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // Enable Outlines export
- uno::Sequence<beans::PropertyValue> aFilterData(
- comphelper::InitPropertySequence({ { "ExportBookmarks", uno::Any(true) } }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- // Parse the export result.
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- auto* pCatalog = aDocument.GetCatalog();
- CPPUNIT_ASSERT(pCatalog);
- auto* pCatalogDictionary = pCatalog->GetDictionary();
- CPPUNIT_ASSERT(pCatalogDictionary);
- auto* pOutlinesObject = pCatalogDictionary->LookupObject("Outlines");
- CPPUNIT_ASSERT(pOutlinesObject);
- auto* pOutlinesDictionary = pOutlinesObject->GetDictionary();
-#if 0
- // Type isn't actually written currently
- auto* pType
- = dynamic_cast<vcl::filter::PDFNameElement*>(pOutlinesDictionary->LookupElement("Type"));
- CPPUNIT_ASSERT(pType);
- CPPUNIT_ASSERT_EQUAL(OString("Outlines"), pType->GetValue());
-#endif
-
- auto* pFirst = dynamic_cast<vcl::filter::PDFReferenceElement*>(
- pOutlinesDictionary->LookupElement("First"));
- CPPUNIT_ASSERT(pFirst);
- auto* pFirstD = pFirst->LookupObject()->GetDictionary();
- CPPUNIT_ASSERT(pFirstD);
- //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirstD->LookupElement("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(u"Preface"_ustr, ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(
- pFirstD->LookupElement("Title"))));
-
- auto* pFirst1
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirstD->LookupElement("First"));
- CPPUNIT_ASSERT(pFirst1);
- auto* pFirst1D = pFirst1->LookupObject()->GetDictionary();
- CPPUNIT_ASSERT(pFirst1D);
- // here is a hidden section with headings "Copyright" etc.; check that
- // there are no outline entries for it
- //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst1D->LookupElement("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- u"Who is this book for?"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst1D->LookupElement("Title"))));
-
- auto* pFirst2
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirst1D->LookupElement("Next"));
- auto* pFirst2D = pFirst2->LookupObject()->GetDictionary();
- CPPUNIT_ASSERT(pFirst2D);
- //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst2D->LookupElement("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- u"What\u2019s in this book?"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst2D->LookupElement("Title"))));
-
- auto* pFirst3
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirst2D->LookupElement("Next"));
- auto* pFirst3D = pFirst3->LookupObject()->GetDictionary();
- CPPUNIT_ASSERT(pFirst3D);
- //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst3D->LookupElement("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- u"Minimum requirements for using LibreOffice"_ustr,
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst3D->LookupElement("Title"))));
-
- CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement*>(nullptr),
- pFirst3D->LookupElement("Next"));
- CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement*>(nullptr),
- pFirstD->LookupElement("Next"));
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageRotate180)
-{
- // Create an empty document.
- mxComponent = loadFromDesktop("private:factory/swriter");
- uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
- uno::Reference<text::XText> xText = xTextDocument->getText();
- uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
-
- // Insert the PDF image.
- uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference<beans::XPropertySet> xGraphicObject(
- xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
- OUString aURL = createFileURL(u"pdf-image-rotate-180.pdf");
- xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
- uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
- xShape->setSize(awt::Size(1000, 1000));
- uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
- xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
-
- // Save as PDF.
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- // Parse the export result.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
-
- // Make sure that the page -> form -> form has a child image.
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
- // 2: white background and the actual object.
- CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(1);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pFormObject->getType());
- CPPUNIT_ASSERT_EQUAL(1, pFormObject->getFormObjectCount());
-
- // Check if the inner form object (original page object in the pdf image) has the correct
- // rotation.
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject = pFormObject->getFormObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pInnerFormObject->getType());
- CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject->getFormObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pImage = pInnerFormObject->getFormObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pImage->getType());
- basegfx::B2DHomMatrix aMat = pInnerFormObject->getMatrix();
- basegfx::B2DTuple aScale;
- basegfx::B2DTuple aTranslate;
- double fRotate = 0;
- double fShearX = 0;
- aMat.decompose(aScale, aTranslate, fRotate, fShearX);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: -1
- // - Actual : 1
- // i.e. the 180 degrees rotation didn't happen (via a combination of horizontal + vertical
- // flip).
- CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, aScale.getX(), 0.01);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf144222)
-{
-// Assume Windows has the font for U+4E2D
-#ifdef _WIN32
- aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
- saveAsPDF(u"tdf144222.ods");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- // The document has one page.
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
- CPPUNIT_ASSERT(pTextPage);
-
- int nPageObjectCount = pPdfPage->getObjectCount();
- const OUString sChar = u"\u4E2D"_ustr;
- basegfx::B2DRectangle aRect1, aRect2;
- int nCount = 0;
-
- for (int i = 0; i < nPageObjectCount; ++i)
- {
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPdfPageObject = pPdfPage->getObject(i);
- if (pPdfPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
- {
- ++nCount;
- OUString sText = pPdfPageObject->getText(pTextPage);
- if (sText == sChar)
- aRect1 = pPdfPageObject->getBounds();
- else
- aRect2 = pPdfPageObject->getBounds();
- }
- }
-
- CPPUNIT_ASSERT_EQUAL(2, nCount);
- CPPUNIT_ASSERT(!aRect1.isEmpty());
- CPPUNIT_ASSERT(!aRect2.isEmpty());
- CPPUNIT_ASSERT(!aRect1.overlaps(aRect2));
-#endif
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf145873)
-{
- // Import the bugdoc and export as PDF.
- aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
- saveAsPDF(u"tdf145873.pptx");
-
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- // The document has one page.
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
-
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- int nPageObjectCount = pPdfPage->getObjectCount();
-
- // tdf#145873: Without the fix #1 in place, this test would have failed with
- // - Expected: 66
- // - Actual : 3
- CPPUNIT_ASSERT_EQUAL(66, nPageObjectCount);
-
- auto pObject = pPdfPage->getObject(4);
- CPPUNIT_ASSERT_MESSAGE("no object", pObject != nullptr);
-
- // tdf#145873: Without the fix #2 in place, this test would have failed with
- // - Expected: 13.40
- // - Actual : 3.57...
- // - Delta : 0.1
- CPPUNIT_ASSERT_DOUBLES_EQUAL(13.40, pObject->getBounds().getWidth(), 0.1);
- // - Expected: 13.79
- // - Actual : 3.74...
- // - Delta : 0.1
- CPPUNIT_ASSERT_DOUBLES_EQUAL(13.79, pObject->getBounds().getHeight(), 0.1);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageHyperlink)
-{
- // Given a Draw file, containing a PDF image, which has a hyperlink in it:
- aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
-
- // When saving to PDF:
- saveAsPDF(u"pdf-image-hyperlink.odg");
-
- // Then make sure that link is preserved:
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- // Without the accompanying fix in place, this test would have failed, the hyperlink of the PDF
- // image was lost.
- CPPUNIT_ASSERT(pPdfPage->hasLinks());
-
- // Also test the precision of the form XObject.
- // Given a full-page form XObject, page height is 27.94 cm (792 points):
- // When writing the reciprocal of the object height to PDF:
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject;
- for (int i = 0; i < pPdfPage->getObjectCount(); ++i)
- {
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pObject = pPdfPage->getObject(i);
- if (pObject->getType() == vcl::pdf::PDFPageObjectType::Form)
- {
- pFormObject = std::move(pObject);
- break;
- }
- }
- CPPUNIT_ASSERT(pFormObject);
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject;
- for (int i = 0; i < pFormObject->getFormObjectCount(); ++i)
- {
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pObject = pFormObject->getFormObject(i);
- if (pObject->getType() == vcl::pdf::PDFPageObjectType::Form)
- {
- pInnerFormObject = std::move(pObject);
- break;
- }
- }
- CPPUNIT_ASSERT(pInnerFormObject);
- // Then make sure that enough digits are used, so the point size is unchanged:
- basegfx::B2DHomMatrix aMatrix = pInnerFormObject->getMatrix();
- basegfx::B2DTuple aScale;
- basegfx::B2DTuple aTranslate;
- double fRotate{};
- double fShearX{};
- aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 0.0012626264
- // - Actual : 0.00126
- // i.e. the rounded reciprocal was 794 points, not the original 792.
- CPPUNIT_ASSERT_EQUAL(0.0012626264, rtl::math::round(aScale.getY(), 10));
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testURIs)
-{
- struct
- {
- OUString in;
- OString out;
- bool relativeFsys;
- } URIs[] = { {
- "http://example.com/",
- "http://example.com/",
- true,
- },
- {
- "file://localfile.odt/",
- "file://localfile.odt/",
- true,
- },
- {
- // tdf 143216
- "http://username:password@example.com",
- "http://username:password@example.com",
- true,
- },
- {
- "git://git.example.org/project/example",
- "git://git.example.org/project/example",
- true,
- },
- {
- // The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
- "filebypath.odt",
- "filebypath.pdf",
- true,
- },
- {
- // The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
- // but this time with ExportLinksRelativeFsys off the path is added
- "filebypath.odt",
- OUStringToOString(utl::GetTempNameBaseDirectory(), RTL_TEXTENCODING_UTF8)
- + "filebypath.pdf",
- false,
- },
- {
- // This also gets made relative due to 'ExportLinksRelativeFsys'
- utl::GetTempNameBaseDirectory() + "fileintempdir.odt",
- "fileintempdir.pdf",
- true,
- } };
-
- // Create an empty document.
- // Note: The test harness gets very upset if we try and create multiple
- // documents, or recreate it; so reuse one instance for all the links
- mxComponent = loadFromDesktop("private:factory/swriter");
- uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
- uno::Reference<text::XText> xText = xTextDocument->getText();
- uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
- xText->insertString(xCursor, "Test pdf", /*bAbsorb=*/false);
-
- // Set the name so it can do relative name replacement
- uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
- xModel->attachResource(maTempFile.GetURL(), xModel->getArgs());
-
- for (unsigned int i = 0; i < (sizeof(URIs) / sizeof(URIs[0])); i++)
- {
- // Test the filename rewriting
- uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({
- { "ExportLinksRelativeFsys", uno::Any(URIs[i].relativeFsys) },
- { "ConvertOOoTargetToPDFTarget", uno::Any(true) },
- }));
- aMediaDescriptor["FilterData"] <<= aFilterData;
-
- // Add a link (based on testNestedHyperlink in rtfexport3)
- xCursor->gotoStart(/*bExpand=*/false);
- xCursor->gotoEnd(/*bExpand=*/true);
- uno::Reference<beans::XPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
- xCursorProps->setPropertyValue("HyperLinkURL", uno::Any(URIs[i].in));
-
- // Save as PDF.
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- // Use the filter rather than the pdfium route, as per the tdf105093 test, it's
- // easier to parse the annotations
- vcl::filter::PDFDocument aDocument;
-
- // Parse the export result.
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
- auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
- CPPUNIT_ASSERT(pAnnots);
-
- // There should be one annotation
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
- auto pAnnotReference
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
- CPPUNIT_ASSERT(pAnnotReference);
- vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
- CPPUNIT_ASSERT(pAnnot);
- // We're expecting something like /Type /Annot /A << /Type /Action /S /URI /URI (path)
- CPPUNIT_ASSERT_EQUAL(
- OString("Annot"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- OString("Link"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
- auto pAction = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
- CPPUNIT_ASSERT(pAction);
- auto pURIElem
- = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAction->LookupElement("URI"));
- CPPUNIT_ASSERT(pURIElem);
- // Check it matches
- CPPUNIT_ASSERT_EQUAL(URIs[i].out, pURIElem->GetValue());
- // tdf#148934 check a11y
- CPPUNIT_ASSERT_EQUAL(
- OUString("Test pdf"),
- ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
- *dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"))));
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageAnnots)
-{
- // Given a document with a PDF image that has 2 comments (popup, text) and a hyperlink:
- aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
-
- // When saving to PDF:
- saveAsPDF(u"pdf-image-annots.odg");
-
- // Then make sure only the hyperlink is kept, since Draw itself has its own comments:
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 1
- // - Actual : 3
- // i.e. not only the hyperlink but also the 2 comments were exported, leading to duplication.
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testPdfImageEncryption)
-{
- // Given an empty document, with an inserted PDF image:
- mxComponent = loadFromDesktop("private:factory/swriter");
- uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
- uno::Reference<text::XText> xText = xTextDocument->getText();
- uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
- uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
- uno::Reference<beans::XPropertySet> xGraphicObject(
- xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
- OUString aURL = createFileURL(u"rectangles.pdf");
- xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
- uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
- xShape->setSize(awt::Size(1000, 1000));
- uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
- xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
-
- // When saving as encrypted PDF:
- uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- uno::Sequence<beans::PropertyValue> aFilterData = {
- comphelper::makePropertyValue("EncryptFile", true),
- comphelper::makePropertyValue("DocumentOpenPassword", OUString("secret")),
- };
- aMediaDescriptor["FilterData"] <<= aFilterData;
- xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
-
- // Then make sure that the image is not lost:
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport("secret");
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 2
- // - Actual : 0
- // i.e. instead of the white background and the actual form child, the image was lost due to
- // missing encryption.
- CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testBitmapScaledown)
-{
- // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
- // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
- if (!IsDefaultDPI())
- return;
-
- // Given a document with an upscaled and rotated barcode bitmap in it:
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
-
- // When saving as PDF:
- saveAsPDF(u"bitmap-scaledown.odt");
-
- // Then verify that the bitmap is not downscaled:
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
- int nPageObjectCount = pPdfPage->getObjectCount();
- for (int i = 0; i < nPageObjectCount; ++i)
- {
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(i);
- if (pPageObject->getType() != vcl::pdf::PDFPageObjectType::Image)
- continue;
-
- std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
- CPPUNIT_ASSERT(pBitmap);
- // In-file sizes: good is 2631x380, bad is 1565x14.
- int nWidth = pBitmap->getWidth();
- // Without the accompanying fix in place, this test would have failed with:
- // - Expected: 2616
- // - Actual : 1565
- // i.e. the bitmap in the pdf result was small enough to be blurry.
- CPPUNIT_ASSERT_EQUAL(2616, nWidth);
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf139627)
-{
-#if HAVE_MORE_FONTS
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- saveAsPDF(u"justified-arabic-kashida.odt");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- // The document has one page.
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
- std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
- CPPUNIT_ASSERT(pPdfPage);
-
- // 7 objects, 3 text, others are path
- int nPageObjectCount = pPdfPage->getObjectCount();
- CPPUNIT_ASSERT_EQUAL(7, nPageObjectCount);
-
- // 3 text objects
- OUString sText[3];
-
- /* With "Noto Sans Arabic" font, these are the X ranges on Linux:
- 0: ( 61.75 - 415.94)
- 1: (479.70 - 422.40)
- 2: (209.40 - 453.2)
- */
- basegfx::B2DRectangle aRect[3];
-
- std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject;
-
- int nTextObjectCount = 0;
- for (int i = 0; i < nPageObjectCount; ++i)
- {
- pPageObject = pPdfPage->getObject(i);
- CPPUNIT_ASSERT_MESSAGE("no object", pPageObject != nullptr);
- if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
- {
- sText[nTextObjectCount] = pPageObject->getText(pTextPage);
- aRect[nTextObjectCount] = pPageObject->getBounds();
- ++nTextObjectCount;
- }
- }
- CPPUNIT_ASSERT_EQUAL(3, nTextObjectCount);
-
- // Text: جِـرم (which means "mass" in Persian)
- // Rendered as (left to right): "reh + mim" - "kasreh" - "jeh + tatweel"
- int rehmim = 0, kasreh = 1, jehtatweel = 2;
-
- CPPUNIT_ASSERT_EQUAL(u"رم"_ustr, sText[rehmim].trim());
- CPPUNIT_ASSERT_EQUAL(u""_ustr, sText[kasreh].trim());
- CPPUNIT_ASSERT_EQUAL(u""_ustr, sText[jehtatweel].trim());
-
- // "Kasreh" should be within "jeh" character
- CPPUNIT_ASSERT_GREATER(aRect[jehtatweel].getMinX(), aRect[kasreh].getMinX());
- CPPUNIT_ASSERT_LESS(aRect[jehtatweel].getMaxX(), aRect[kasreh].getMaxX());
-
- // "Tatweel" should cover "jeh" and "reh"+"mim" to avoid gap
- // Checking right gap
- //CPPUNIT_ASSERT_GREATER(aRect[jehtatweel].getMinX(), aRect[tatweel].getMaxX());
- // Checking left gap
- // Kashida fails to reach to rehmim before the series of patches starting
- // with 3901e029bd39575f700e69a73818565d62226a23. The visible symptom is
- // a gap in the left of Kashida.
- CPPUNIT_ASSERT_LESS(aRect[rehmim].getMaxX(), aRect[jehtatweel].getMinX());
-
- // Overlappings of Kashida and surrounding characters is ~9% of the width
- // of the "jeh" character, while using "Noto Arabic Sans" font in this
- // specific example.
- // We set the hard limit of 10% here.
- CPPUNIT_ASSERT_LESS(0.1, fabs(aRect[rehmim].getMaxX() - aRect[jehtatweel].getMinX())
- / aRect[jehtatweel].getWidth());
-#endif
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testRexportRefToKids)
-{
- // We need to enable PDFium import (and make sure to disable after the test)
- bool bResetEnvVar = false;
- if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
- {
- bResetEnvVar = true;
- osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
- }
- comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
- if (bResetEnvVar)
- osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
- });
-
- // Load the PDF and save as PDF
- vcl::filter::PDFDocument aDocument;
- load(u"ref-to-kids.pdf", aDocument);
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
-
- vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
- CPPUNIT_ASSERT(pResources);
-
- auto pXObjects
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
- CPPUNIT_ASSERT(pXObjects);
-
- // Without the fix LookupObject for all /Im's will fail.
- for (auto const& rPair : pXObjects->GetItems())
- {
- if (rPair.first.startsWith("Im"))
- CPPUNIT_ASSERT(pXObjects->LookupObject(rPair.first));
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testRexportFilterSingletonArray)
-{
- // We need to enable PDFium import (and make sure to disable after the test)
- bool bResetEnvVar = false;
- if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
- {
- bResetEnvVar = true;
- osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
- }
- comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
- if (bResetEnvVar)
- osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
- });
-
- // Load the PDF and save as PDF
- vcl::filter::PDFDocument aDocument;
- // Loading fails with tagged PDF enabled
- load(u"ref-to-kids.pdf", aDocument, false);
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
-
- // Directly go to the inner XObject Im5 that contains the rectangle drawings.
- auto pInnerIm = aDocument.LookupObject(5);
- CPPUNIT_ASSERT(pInnerIm);
-
- auto pFilter = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerIm->Lookup("Filter"));
- CPPUNIT_ASSERT(pFilter);
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Filter must be FlateDecode", OString("FlateDecode"),
- pFilter->GetValue());
-
- vcl::filter::PDFStreamElement* pStream = pInnerIm->GetStream();
- CPPUNIT_ASSERT(pStream);
- SvMemoryStream& rObjectStream = pStream->GetMemory();
- // Uncompress it.
- SvMemoryStream aUncompressed;
- ZCodec aZCodec;
- aZCodec.BeginCompression();
- rObjectStream.Seek(0);
- aZCodec.Decompress(rObjectStream, aUncompressed);
- CPPUNIT_ASSERT(aZCodec.EndCompression());
-
- // Without the fix, the stream is doubly compressed,
- // hence one decompression will not yield the "re" expressions.
- auto pStart = static_cast<const char*>(aUncompressed.GetData());
- const char* pEnd = pStart + aUncompressed.GetSize();
- OString aImage = "100 0 30 50 re B*\n70 67 50 30 re B*\n";
- auto it = std::search(pStart, pEnd, aImage.getStr(), aImage.getStr() + aImage.getLength());
- CPPUNIT_ASSERT(it != pEnd);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testRexportMediaBoxOrigin)
-{
- // We need to enable PDFium import (and make sure to disable after the test)
- bool bResetEnvVar = false;
- if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
- {
- bResetEnvVar = true;
- osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
- }
- comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
- if (bResetEnvVar)
- osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
- });
-
- // Load the PDF and save as PDF
- vcl::filter::PDFDocument aDocument;
- load(u"ref-to-kids.pdf", aDocument);
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
-
- // Directly go to the inner XObject Im12 that contains the rectangle drawings in page 2.
- auto pInnerIm = aDocument.LookupObject(12);
- CPPUNIT_ASSERT(pInnerIm);
-
- constexpr sal_Int32 aOrigin[2] = { -800, -600 };
- sal_Int32 aSize[2] = { 0, 0 };
-
- auto pBBox = dynamic_cast<vcl::filter::PDFArrayElement*>(pInnerIm->Lookup("BBox"));
- CPPUNIT_ASSERT(pBBox);
- const auto& rElements2 = pBBox->GetElements();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements2.size());
- for (sal_Int32 nIdx = 0; nIdx < 4; ++nIdx)
- {
- const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements2[nIdx]);
- CPPUNIT_ASSERT(pNumElement);
- if (nIdx < 2)
- CPPUNIT_ASSERT_EQUAL(aOrigin[nIdx], static_cast<sal_Int32>(pNumElement->GetValue()));
- else
- aSize[nIdx - 2] = static_cast<sal_Int32>(pNumElement->GetValue()) - aOrigin[nIdx - 2];
- }
-
- auto pMatrix = dynamic_cast<vcl::filter::PDFArrayElement*>(pInnerIm->Lookup("Matrix"));
- CPPUNIT_ASSERT(pMatrix);
- const auto& rElements = pMatrix->GetElements();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(6), rElements.size());
- sal_Int32 aMatTranslate[6]
- = { // Rotation by $\theta$ $cos(\theta), sin(\theta), -sin(\theta), cos(\theta)$
- 0, -1, 1, 0,
- // Translate x,y
- -aOrigin[1] - aSize[1] / 2 + aSize[0] / 2, aOrigin[0] + aSize[0] / 2 + aSize[1] / 2
- };
-
- for (sal_Int32 nIdx = 0; nIdx < 6; ++nIdx)
- {
- const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements[nIdx]);
- CPPUNIT_ASSERT(pNumElement);
- CPPUNIT_ASSERT_EQUAL(aMatTranslate[nIdx], static_cast<sal_Int32>(pNumElement->GetValue()));
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testRexportResourceItemReference)
-{
- // We need to enable PDFium import (and make sure to disable after the test)
- bool bResetEnvVar = false;
- if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
- {
- bResetEnvVar = true;
- osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
- }
- comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
- if (bResetEnvVar)
- osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
- });
-
- // Load the PDF and save as PDF
- vcl::filter::PDFDocument aDocument;
- load(u"ref-to-kids.pdf", aDocument);
-
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
-
- // Directly go to the inner XObject Im12 that has reference to Font in page 2.
- auto pInnerIm = aDocument.LookupObject(12);
- CPPUNIT_ASSERT(pInnerIm);
-
- auto pResources
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pInnerIm->Lookup("Resources"));
- CPPUNIT_ASSERT(pResources);
- auto pFontsReference
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pResources->LookupElement("Font"));
- CPPUNIT_ASSERT(pFontsReference);
-
- auto pFontsObject = pFontsReference->LookupObject();
- CPPUNIT_ASSERT(pFontsObject);
-
- auto pFontDict
- = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pFontsObject->Lookup("FF132"));
- CPPUNIT_ASSERT(pFontDict);
-
- auto pFontDescriptor = pFontDict->LookupObject("FontDescriptor");
- CPPUNIT_ASSERT(pFontDescriptor);
-
- auto pFontWidths = pFontDict->LookupObject("Widths");
- CPPUNIT_ASSERT(pFontWidths);
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf152246)
-{
- // Import the bugdoc and export as PDF.
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- saveAsPDF(u"content-control-rtl.docx");
-
- // Parse the export result.
- vcl::filter::PDFDocument aDocument;
- SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
- CPPUNIT_ASSERT(aDocument.Read(aStream));
-
- // The document has one page.
- std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
-
- // Position array
- constexpr double aPos[5][4] = { { 56.699, 707.701, 131.401, 721.499 },
- { 198.499, 707.701, 273.201, 721.499 },
- { 303.349, 680.101, 378.051, 693.899 },
- { 480.599, 680.101, 555.301, 693.899 },
- { 56.699, 652.501, 131.401, 666.299 } };
-
- // Get page annotations.
- auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
- CPPUNIT_ASSERT(pAnnots);
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), pAnnots->GetElements().size());
- for (sal_Int32 i = 0; i < 5; ++i)
- {
- auto pAnnotReference
- = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[i]);
- CPPUNIT_ASSERT(pAnnotReference);
- vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
- CPPUNIT_ASSERT(pAnnot);
- CPPUNIT_ASSERT_EQUAL(
- OString("Annot"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
- CPPUNIT_ASSERT_EQUAL(
- OString("Widget"),
- static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
-
- auto pRect = dynamic_cast<vcl::filter::PDFArrayElement*>(pAnnot->Lookup("Rect"));
- CPPUNIT_ASSERT(pRect);
- const auto& rElements = pRect->GetElements();
- CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements.size());
- for (sal_Int32 nIdx = 0; nIdx < 4; ++nIdx)
- {
- const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements[nIdx]);
- CPPUNIT_ASSERT(pNumElement);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(aPos[i][nIdx], pNumElement->GetValue(), 1e-6);
- }
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf155161)
-{
-// TODO: We seem to get a fallback font on Windows
-#ifndef _WIN32
- vcl::filter::PDFDocument aDocument;
- load(u"tdf155161.odt", aDocument);
-
- // Check that all fonts in the document are Type 3 fonts
- int nFonts = 0;
- for (const auto& aElement : aDocument.GetElements())
- {
- auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(aElement.get());
- if (!pObject)
- continue;
- auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
- if (pType && pType->GetValue() == "Font")
- {
- auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Subtype"));
- CPPUNIT_ASSERT(pSubtype);
- CPPUNIT_ASSERT_EQUAL(OString("Type3"), pSubtype->GetValue());
-
- auto pName = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Name"));
- CPPUNIT_ASSERT(pName);
- CPPUNIT_ASSERT_EQUAL(OString("Cantarell-Regular"), pName->GetValue());
-
- nFonts++;
- }
- }
-
-#ifdef MACOSX
- // There must be two fonts
- CPPUNIT_ASSERT_EQUAL(2, nFonts);
-#else
- // But it seems that embedded variable fonts don’t register all supported
- // styles on Linux, so the bold and regular text use the same regular font.
- CPPUNIT_ASSERT(nFonts);
-#endif
-#endif
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf48707_1)
-{
- // Import the bugdoc and export as PDF.
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- saveAsPDF(u"tdf48707-1.fodt");
-
- // Parse the export result with pdfium.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
-
- auto pPage = pPdfDocument->openPage(0);
- CPPUNIT_ASSERT(pPage);
-
- int nPageObjectCount = pPage->getObjectCount();
-
- CPPUNIT_ASSERT_EQUAL(6, nPageObjectCount);
-
- auto pTextPage = pPage->getTextPage();
-
- for (int i = 0; i < nPageObjectCount; ++i)
- {
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(i);
- // The text and path objects (underline and overline) should all be red.
- if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
- CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getFillColor());
- else
- CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getStrokeColor());
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf48707_2)
-{
- // Import the bugdoc and export as PDF.
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- saveAsPDF(u"tdf48707-2.fodt");
-
- // Parse the export result with pdfium.
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
-
- auto pPage = pPdfDocument->openPage(0);
- CPPUNIT_ASSERT(pPage);
-
- int nPageObjectCount = pPage->getObjectCount();
-
- CPPUNIT_ASSERT_EQUAL(13, nPageObjectCount);
-
- auto pTextPage = pPage->getTextPage();
-
- for (int i = 0; i < nPageObjectCount; ++i)
- {
- std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(i);
- if (pPageObject->getType() != vcl::pdf::PDFPageObjectType::Path)
- continue;
-
- // The table-like paths should be red, underline and overline should be black.
- if (i >= 8)
- CPPUNIT_ASSERT_EQUAL(COL_BLACK, pPageObject->getStrokeColor());
- else
- CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getStrokeColor());
- }
-}
-
-CPPUNIT_TEST_FIXTURE(PdfExportTest, testTdf156528)
-{
- aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
- saveAsPDF(u"wide_page1.fodt");
- std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
-
- // The document has two pages
- CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
-
- // 1st page (5100 mm width x 210 mm high, UserUnit = 2)
- auto pPdfPage = pPdfDocument->openPage(0);
- CPPUNIT_ASSERT(pPdfPage);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5100.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
- pPdfPage->getWidth(), 1);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
- pPdfPage->getHeight(), 1);
-
- // 1 object (rectangle 5060 mm width x 170 mm high, UserUnit = 2)
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
- auto pRect = pPdfPage->getObject(0);
- CPPUNIT_ASSERT(pRect);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path, pRect->getType());
- auto bounds = pRect->getBounds();
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5060.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
- bounds.getWidth(), 1);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
- bounds.getHeight(), 1);
-
- // 2nd page (210 mm width x 297 mm high, UserUnit = 1)
- pPdfPage = pPdfDocument->openPage(1);
- CPPUNIT_ASSERT(pPdfPage);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0, o3tl::Length::mm, o3tl::Length::pt),
- pPdfPage->getWidth(), 1);
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(297.0, o3tl::Length::mm, o3tl::Length::pt),
- pPdfPage->getHeight(), 1);
-
- // 1 object (rectangle 170 mm width x 257 mm high, UserUnit = 1)
- CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
- pRect = pPdfPage->getObject(0);
- CPPUNIT_ASSERT(pRect);
- CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path, pRect->getType());
- bounds = pRect->getBounds();
- // Without the fix, this would fail with
- // - Expected: 481.889763779528
- // - Actual : 241.925001144409
- // - Delta : 1
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0, o3tl::Length::mm, o3tl::Length::pt),
- bounds.getWidth(), 1);
- //
- // - Expected: 728.503937007874
- // - Actual : 365.25
- // - Delta : 1
- CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(257.0, o3tl::Length::mm, o3tl::Length::pt),
- bounds.getHeight(), 1);
-}
-
} // end anonymous namespace
CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/vcl/qa/cppunit/pdfexport/pdfexport2.cxx b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
new file mode 100644
index 000000000000..bfdd1ae8ea31
--- /dev/null
+++ b/vcl/qa/cppunit/pdfexport/pdfexport2.cxx
@@ -0,0 +1,4802 @@
+/* -*- 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 <sal/config.h>
+
+#include <memory>
+#include <string_view>
+
+#include <config_fonts.h>
+#include <osl/process.h>
+
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <test/unoapi_test.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/tempfile.hxx>
+#include <vcl/filter/pdfdocument.hxx>
+#include <tools/zcodec.hxx>
+#include <tools/XmlWalker.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <unotools/streamwrap.hxx>
+#include <rtl/math.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Tests the PDF export filter.
+class PdfExportTest2 : public UnoApiTest
+{
+protected:
+ utl::MediaDescriptor aMediaDescriptor;
+
+public:
+ PdfExportTest2()
+ : UnoApiTest("/vcl/qa/cppunit/pdfexport/data/")
+ {
+ }
+
+ void saveAsPDF(std::u16string_view rFile);
+ void load(std::u16string_view rFile, vcl::filter::PDFDocument& rDocument,
+ bool bUseTaggedPDF = true);
+};
+
+void PdfExportTest2::saveAsPDF(std::u16string_view rFile)
+{
+ // Import the bugdoc and export as PDF.
+ loadFromURL(rFile);
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+}
+
+void PdfExportTest2::load(std::u16string_view rFile, vcl::filter::PDFDocument& rDocument,
+ bool bUseTaggedPDF)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "UseTaggedPDF", uno::Any(bUseTaggedPDF) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(rFile);
+
+ // Parse the export result.
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(rDocument.Read(aStream));
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf124272)
+{
+ // Import the bugdoc and export as PDF.
+ vcl::filter::PDFDocument aDocument;
+ load(u"tdf124272.odt", aDocument);
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ // The page has a stream.
+ vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
+ CPPUNIT_ASSERT(pContents);
+ vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ // Uncompress it.
+ SvMemoryStream aUncompressed;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ rObjectStream.Seek(0);
+ aZCodec.Decompress(rObjectStream, aUncompressed);
+ CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+ OString aBitmap("Q q 299.899 782.189 m\n"
+ "55.2 435.889 l 299.899 435.889 l 299.899 782.189 l\n"
+ "h");
+
+ auto pStart = static_cast<const char*>(aUncompressed.GetData());
+ const char* pEnd = pStart + aUncompressed.GetSize();
+ auto it = std::search(pStart, pEnd, aBitmap.getStr(), aBitmap.getStr() + aBitmap.getLength());
+ CPPUNIT_ASSERT(it != pEnd);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf121615)
+{
+ vcl::filter::PDFDocument aDocument;
+ load(u"tdf121615.odt", aDocument);
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ // Get access to the only image on the only page.
+ vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
+ CPPUNIT_ASSERT(pResources);
+ auto pXObjects
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
+ CPPUNIT_ASSERT(pXObjects);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
+ vcl::filter::PDFObjectElement* pXObject
+ = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pXObject);
+ vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+
+ // Load the embedded image.
+ rObjectStream.Seek(0);
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ Graphic aGraphic;
+ sal_uInt16 format;
+ ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
+ GRFILTER_FORMAT_DONTKNOW, &format);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
+
+ // The image should be grayscale 8bit JPEG.
+ sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
+ CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
+ CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
+ BitmapEx aBitmap = aGraphic.GetBitmapEx();
+ CPPUNIT_ASSERT_EQUAL(tools::Long(200), aBitmap.GetSizePixel().Width());
+ CPPUNIT_ASSERT_EQUAL(tools::Long(300), aBitmap.GetSizePixel().Height());
+ CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, aBitmap.getPixelFormat());
+ // tdf#121615 was caused by broken handling of data width with 8bit color,
+ // so the test image has some black in the bottomright corner, check it's there
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(0, 0));
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(0, 299));
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(199, 0));
+ CPPUNIT_ASSERT_EQUAL(COL_BLACK, aBitmap.GetPixelColor(199, 299));
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf141171)
+{
+ vcl::filter::PDFDocument aDocument;
+ load(u"tdf141171.odt", aDocument);
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ // Get access to the only image on the only page.
+ vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
+ CPPUNIT_ASSERT(pResources);
+ auto pXObjects
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
+ CPPUNIT_ASSERT(pXObjects);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
+ vcl::filter::PDFObjectElement* pXObject
+ = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pXObject);
+ vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+
+ // Load the embedded image.
+ rObjectStream.Seek(0);
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ Graphic aGraphic;
+ sal_uInt16 format;
+ ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
+ GRFILTER_FORMAT_DONTKNOW, &format);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
+
+ // The image should be grayscale 8bit JPEG.
+ sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
+ CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
+ CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
+ BitmapEx aBitmap = aGraphic.GetBitmapEx();
+ Size aSize = aBitmap.GetSizePixel();
+ CPPUNIT_ASSERT_EQUAL(tools::Long(878), aSize.Width());
+ CPPUNIT_ASSERT_EQUAL(tools::Long(127), aSize.Height());
+ CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP, aBitmap.getPixelFormat());
+
+ for (tools::Long nX = 0; nX < aSize.Width(); ++nX)
+ {
+ for (tools::Long nY = 0; nY < aSize.Height(); ++nY)
+ {
+ // Check all pixels in the image are white
+ // Without the fix in place, this test would have failed with
+ // - Expected: Color: R:255 G:255 B:255 A:0
+ // - Actual : Color: R:0 G:0 B:0 A:0
+ CPPUNIT_ASSERT_EQUAL(COL_WHITE, aBitmap.GetPixelColor(nX, nY));
+ }
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf129085)
+{
+ vcl::filter::PDFDocument aDocument;
+ load(u"tdf129085.docx", aDocument);
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ // Get access to the only image on the only page.
+ vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
+ CPPUNIT_ASSERT(pResources);
+ auto pXObjects
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
+
+ // Without the fix in place, this test would have failed here
+ CPPUNIT_ASSERT(pXObjects);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects->GetItems().size());
+ vcl::filter::PDFObjectElement* pXObject
+ = pXObjects->LookupObject(pXObjects->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pXObject);
+ vcl::filter::PDFStreamElement* pStream = pXObject->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+
+ // Load the embedded image.
+ rObjectStream.Seek(0);
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ Graphic aGraphic;
+ sal_uInt16 format;
+ ErrCode bResult = rFilter.ImportGraphic(aGraphic, u"import", rObjectStream,
+ GRFILTER_FORMAT_DONTKNOW, &format);
+ CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, bResult);
+
+ sal_uInt16 jpegFormat = rFilter.GetImportFormatNumberForShortName(JPG_SHORTNAME);
+ CPPUNIT_ASSERT(jpegFormat != GRFILTER_FORMAT_NOTFOUND);
+ CPPUNIT_ASSERT_EQUAL(jpegFormat, format);
+ BitmapEx aBitmap = aGraphic.GetBitmapEx();
+ CPPUNIT_ASSERT_EQUAL(tools::Long(884), aBitmap.GetSizePixel().Width());
+ CPPUNIT_ASSERT_EQUAL(tools::Long(925), aBitmap.GetSizePixel().Height());
+ CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP, aBitmap.getPixelFormat());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTocLink)
+{
+ // Load the Writer document.
+ loadFromURL(u"toc-link.fodt");
+
+ // Update the ToC.
+ uno::Reference<text::XDocumentIndexesSupplier> xDocumentIndexesSupplier(mxComponent,
+ uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xDocumentIndexesSupplier.is());
+
+ uno::Reference<util::XRefreshable> xToc(
+ xDocumentIndexesSupplier->getDocumentIndexes()->getByIndex(0), uno::UNO_QUERY);
+ CPPUNIT_ASSERT(xToc.is());
+
+ xToc->refresh();
+
+ // Save as PDF.
+ save("writer_pdf_Export");
+
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // Ensure there is a link on the first page (in the ToC).
+ // Without the accompanying fix in place, this test would have failed, as the page contained no
+ // links.
+ CPPUNIT_ASSERT(pPdfPage->hasLinks());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReduceSmallImage)
+{
+ // Load the Writer document.
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ saveAsPDF(u"reduce-small-image.fodt");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pPageObject->getType());
+
+ // Make sure we don't scale down a tiny bitmap.
+ std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
+ CPPUNIT_ASSERT(pBitmap);
+ int nWidth = pBitmap->getWidth();
+ int nHeight = pBitmap->getHeight();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 16
+ // - Actual : 6
+ // i.e. the image was scaled down to 300 DPI, even if it had tiny size.
+ CPPUNIT_ASSERT_EQUAL(16, nWidth);
+ CPPUNIT_ASSERT_EQUAL(16, nHeight);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf114256)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
+ saveAsPDF(u"tdf114256.ods");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // Without the fix in place, this test would have failed with
+ // - Expected: 13
+ // - Actual : 0
+ CPPUNIT_ASSERT_EQUAL(13, pPdfPage->getObjectCount());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf150931)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
+ saveAsPDF(u"tdf150931.ods");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ int nPageObjectCount = pPdfPage->getObjectCount();
+ // Without the fix in place, this test would have failed with
+ // - Expected: 15
+ // - Actual : 16
+ CPPUNIT_ASSERT_EQUAL(16, nPageObjectCount);
+
+ int nYellowPathCount = 0;
+ int nBlackPathCount = 0;
+ int nGrayPathCount = 0;
+ int nRedPathCount = 0;
+ for (int i = 0; i < nPageObjectCount; ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPdfPageObject = pPdfPage->getObject(i);
+ if (pPdfPageObject->getType() != vcl::pdf::PDFPageObjectType::Path)
+ continue;
+
+ int nSegments = pPdfPageObject->getPathSegmentCount();
+ CPPUNIT_ASSERT_EQUAL(5, nSegments);
+
+ if (pPdfPageObject->getFillColor() == COL_YELLOW)
+ ++nYellowPathCount;
+ else if (pPdfPageObject->getFillColor() == COL_BLACK)
+ ++nBlackPathCount;
+ else if (pPdfPageObject->getFillColor() == COL_GRAY)
+ ++nGrayPathCount;
+ else if (pPdfPageObject->getFillColor() == COL_LIGHTRED)
+ ++nRedPathCount;
+ }
+
+ CPPUNIT_ASSERT_EQUAL(3, nYellowPathCount);
+ CPPUNIT_ASSERT_EQUAL(3, nRedPathCount);
+ CPPUNIT_ASSERT_EQUAL(3, nGrayPathCount);
+ CPPUNIT_ASSERT_EQUAL(3, nBlackPathCount);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf147027)
+{
+ // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
+ // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
+ if (!IsDefaultDPI())
+ return;
+
+ // Load the Calc document.
+ aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
+ saveAsPDF(u"tdf147027.ods");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // Without the fix in place, this test would have failed with
+ // - Expected: 778
+ // - Actual : 40
+ CPPUNIT_ASSERT_EQUAL(778, pPdfPage->getObjectCount());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf135346)
+{
+ // Load the Calc document.
+ aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
+ saveAsPDF(u"tdf135346.ods");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // Without the fix in place, this test would have failed with
+ // - Expected: 56
+ // - Actual : 0
+ CPPUNIT_ASSERT_EQUAL(56, pPdfPage->getObjectCount());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf147164)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
+ saveAsPDF(u"tdf147164.odp");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/1);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // Without the fix in place, this test would have failed with
+ // - Expected: 22
+ // - Actual : 16
+ CPPUNIT_ASSERT_EQUAL(22, pPdfPage->getObjectCount());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReduceImage)
+{
+ // Load the Writer document.
+ loadFromURL(u"reduce-image.fodt");
+
+ // Save as PDF.
+ 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);
+
+ SvFileStream aOutputStream(maTempFile.GetURL(), StreamMode::WRITE);
+ uno::Reference<io::XOutputStream> xOutputStream(new utl::OStreamWrapper(aOutputStream));
+
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "ReduceImageResolution", uno::Any(false) } }));
+
+ // This is intentionally in an "unlucky" order, output stream comes before filter data.
+ uno::Sequence<beans::PropertyValue> aDescriptor(comphelper::InitPropertySequence({
+ { "FilterName", uno::Any(OUString("writer_pdf_Export")) },
+ { "OutputStream", uno::Any(xOutputStream) },
+ { "FilterData", uno::Any(aFilterData) },
+ }));
+ xFilter->filter(aDescriptor);
+ aOutputStream.Close();
+
+ // Parse the PDF: get the image.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pPageObject->getType());
+
+ // Make sure we don't scale down a bitmap.
+ std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
+ CPPUNIT_ASSERT(pBitmap);
+ int nWidth = pBitmap->getWidth();
+ int nHeight = pBitmap->getHeight();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 160
+ // - Actual : 6
+ // i.e. the image was scaled down even with ReduceImageResolution=false.
+ CPPUNIT_ASSERT_EQUAL(160, nWidth);
+ CPPUNIT_ASSERT_EQUAL(160, nHeight);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testLinkWrongPage)
+{
+ // Import the bugdoc and export as PDF.
+ aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
+ saveAsPDF(u"link-wrong-page.odp");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ // The document has 2 pages.
+ CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
+
+ // First page should have 1 link (2nd slide, 1st was hidden).
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // Without the accompanying fix in place, this test would have failed, as the link of the first
+ // page went to the second page due to the hidden first slide.
+ CPPUNIT_ASSERT(pPdfPage->hasLinks());
+
+ // Second page should have no links (3rd slide).
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage2 = pPdfDocument->openPage(/*nIndex=*/1);
+ CPPUNIT_ASSERT(pPdfPage2);
+ CPPUNIT_ASSERT(!pPdfPage2->hasLinks());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testLinkWrongPagePartial)
+{
+ // Given a Draw document with 3 pages, a link on the 2nd page:
+ // When exporting that the 2nd and 3rd page to pdf:
+ uno::Sequence<beans::PropertyValue> aFilterData = {
+ comphelper::makePropertyValue("PageRange", OUString("2-3")),
+ };
+ aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"link-wrong-page-partial.odg");
+
+ // Then make sure the we have a link on the 1st page, but not on the 2nd one:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ // Without the accompanying fix in place, this test would have failed, as the link was on the
+ // 2nd page instead.
+ CPPUNIT_ASSERT(pPdfPage->hasLinks());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage2 = pPdfDocument->openPage(/*nIndex=*/1);
+ CPPUNIT_ASSERT(pPdfPage2);
+ CPPUNIT_ASSERT(!pPdfPage2->hasLinks());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPageRange)
+{
+ // Given a document with 3 pages:
+ // When exporting that document to PDF, skipping the first page:
+ aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
+ aMediaDescriptor["FilterOptions"]
+ <<= OUString("{\"PageRange\":{\"type\":\"string\",\"value\":\"2-\"}}");
+ saveAsPDF(u"link-wrong-page-partial.odg");
+
+ // Then make sure the resulting PDF has 2 pages:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 3
+ // i.e. FilterOptions was ignored.
+ CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testLargePage)
+{
+ // Import the bugdoc and export as PDF.
+ aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
+ saveAsPDF(u"6m-wide.odg");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ // The document has 1 page.
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ // Check the value (not the unit) of the page size.
+ basegfx::B2DSize aSize = pPdfDocument->getPageSize(0);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 8503.94
+ // - Actual : 17007.875
+ // i.e. the value for 600 cm was larger than the 14 400 limit set in the spec.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(8503.94, aSize.getWidth(), 0.01);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageResourceInlineXObjectRef)
+{
+ // Create an empty document.
+ mxComponent = loadFromDesktop("private:factory/swriter");
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+
+ // Insert the PDF image.
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xGraphicObject(
+ xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+ OUString aURL = createFileURL(u"pdf-image-resource-inline-xobject-ref.pdf");
+ xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
+ uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
+ xShape->setSize(awt::Size(1000, 1000));
+ uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
+
+ // Save as PDF.
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+ // Make sure that the page -> form -> form has a child image.
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
+ // 2: white background and the actual object.
+ CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(1);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pFormObject->getType());
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 0
+ // i.e. the sub-form was missing its image.
+ CPPUNIT_ASSERT_EQUAL(1, pFormObject->getFormObjectCount());
+
+ // Check if the inner form object (original page object in the pdf image) has the correct
+ // rotation.
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject = pFormObject->getFormObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pInnerFormObject->getType());
+ CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pImage = pInnerFormObject->getFormObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pImage->getType());
+ basegfx::B2DHomMatrix aMat = pInnerFormObject->getMatrix();
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate = 0;
+ double fShearX = 0;
+ aMat.decompose(aScale, aTranslate, fRotate, fShearX);
+ int nRotateDeg = basegfx::rad2deg(fRotate);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: -90
+ // - Actual : 0
+ // i.e. rotation was lost on pdf export.
+ CPPUNIT_ASSERT_EQUAL(-90, nRotateDeg);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testDefaultVersion)
+{
+ // Create an empty document.
+ mxComponent = loadFromDesktop("private:factory/swriter");
+
+ // Save as PDF.
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ int nFileVersion = pPdfDocument->getFileVersion();
+ CPPUNIT_ASSERT_EQUAL(17, nFileVersion);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testVersion15)
+{
+ // Create an empty document.
+ mxComponent = loadFromDesktop("private:factory/swriter");
+
+ // Save as PDF.
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
+ { { "SelectPdfVersion", uno::Any(static_cast<sal_Int32>(15)) } }));
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ int nFileVersion = pPdfDocument->getFileVersion();
+ CPPUNIT_ASSERT_EQUAL(15, nFileVersion);
+}
+
+// Check round-trip of importing and exporting the PDF with PDFium filter,
+// which imports the PDF document as multiple PDFs as graphic object.
+// Each page in the document has one PDF graphic object which content is
+// the corresponding page in the PDF. When such a document is exported,
+// the PDF graphic gets embedded into the exported PDF document (as a
+// Form XObject).
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testMultiPagePDF)
+{
+// setenv only works on unix based systems
+#ifndef _WIN32
+ // We need to enable PDFium import (and make sure to disable after the test)
+ bool bResetEnvVar = false;
+ if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+ {
+ bResetEnvVar = true;
+ setenv("LO_IMPORT_USE_PDFIUM", "1", false);
+ }
+ comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+ if (bResetEnvVar)
+ unsetenv("LO_IMPORT_USE_PDFIUM");
+ });
+
+ // Load the PDF and save as PDF
+ vcl::filter::PDFDocument aDocument;
+ load(u"SimpleMultiPagePDF.pdf", aDocument);
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages.size());
+
+ vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
+ CPPUNIT_ASSERT(pResources);
+
+ auto pXObjects
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
+ CPPUNIT_ASSERT(pXObjects);
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3),
+ pXObjects->GetItems().size()); // 3 PDFs as Form XObjects
+
+ std::vector<OString> rIDs;
+ for (auto const& rPair : pXObjects->GetItems())
+ {
+ rIDs.push_back(rPair.first);
+ }
+
+ // Let's check the embedded PDF pages - just make sure the size differs,
+ // which should indicate we don't have 3 times the same page.
+
+ { // embedded PDF page 1
+ vcl::filter::PDFObjectElement* pXObject1 = pXObjects->LookupObject(rIDs[0]);
+ CPPUNIT_ASSERT(pXObject1);
+ CPPUNIT_ASSERT_EQUAL(OString("Im21"), rIDs[0]);
+
+ auto pSubtype1 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject1->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pSubtype1);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype1->GetValue());
+
+ auto pXObjectResources
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject1->Lookup("Resources"));
+ CPPUNIT_ASSERT(pXObjectResources);
+ auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pXObjectResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pXObjectForms);
+ vcl::filter::PDFObjectElement* pForm
+ = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pForm);
+
+ vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Just check that the size of the page stream is what is expected.
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(230), rObjectStream.remainingSize());
+ }
+
+ { // embedded PDF page 2
+ vcl::filter::PDFObjectElement* pXObject2 = pXObjects->LookupObject(rIDs[1]);
+ CPPUNIT_ASSERT(pXObject2);
+ CPPUNIT_ASSERT_EQUAL(OString("Im27"), rIDs[1]);
+
+ auto pSubtype2 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject2->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pSubtype2);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype2->GetValue());
+
+ auto pXObjectResources
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject2->Lookup("Resources"));
+ CPPUNIT_ASSERT(pXObjectResources);
+ auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pXObjectResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pXObjectForms);
+ vcl::filter::PDFObjectElement* pForm
+ = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pForm);
+
+ vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Just check that the size of the page stream is what is expected
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(309), rObjectStream.remainingSize());
+ }
+
+ { // embedded PDF page 3
+ vcl::filter::PDFObjectElement* pXObject3 = pXObjects->LookupObject(rIDs[2]);
+ CPPUNIT_ASSERT(pXObject3);
+ CPPUNIT_ASSERT_EQUAL(OString("Im5"), rIDs[2]);
+
+ auto pSubtype3 = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject3->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pSubtype3);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype3->GetValue());
+
+ auto pXObjectResources
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject3->Lookup("Resources"));
+ CPPUNIT_ASSERT(pXObjectResources);
+ auto pXObjectForms = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pXObjectResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pXObjectForms);
+ vcl::filter::PDFObjectElement* pForm
+ = pXObjectForms->LookupObject(pXObjectForms->GetItems().begin()->first);
+ CPPUNIT_ASSERT(pForm);
+
+ vcl::filter::PDFStreamElement* pStream = pForm->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ rObjectStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Just check that the size of the page stream is what is expected
+ CPPUNIT_ASSERT_EQUAL(sal_uInt64(193), rObjectStream.remainingSize());
+ }
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFormFontName)
+{
+ // Import the bugdoc and export as PDF.
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ saveAsPDF(u"form-font-name.odt");
+
+ // Parse the export result with pdfium.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ // The document has one page.
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // The page has one annotation.
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount());
+ std::unique_ptr<vcl::pdf::PDFiumAnnotation> pAnnot = pPdfPage->getAnnotation(0);
+
+ // Examine the default appearance.
+ CPPUNIT_ASSERT(pAnnot->hasKey("DA"));
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFObjectType::String, pAnnot->getValueType("DA"));
+ OUString aDA = pAnnot->getString("DA");
+
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 0 0 0 rg /TiRo 12 Tf
+ // - Actual : 0 0 0 rg /F2 12 Tf
+ // i.e. Liberation Serif was exposed as a form font as-is, without picking the closest built-in
+ // font.
+ CPPUNIT_ASSERT_EQUAL(OUString("0 0 0 rg /TiRo 12 Tf"), aDA);
+}
+
+// Check we don't have duplicated objects when we reexport the PDF multiple
+// times or the size will exponentially increase over time.
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReexportPDF)
+{
+// setenv only works on unix based systems
+#ifndef _WIN32
+ // We need to enable PDFium import (and make sure to disable after the test)
+ bool bResetEnvVar = false;
+ if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+ {
+ bResetEnvVar = true;
+ setenv("LO_IMPORT_USE_PDFIUM", "1", false);
+ }
+ comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+ if (bResetEnvVar)
+ unsetenv("LO_IMPORT_USE_PDFIUM");
+ });
+
+ // Load the PDF and save as PDF
+ vcl::filter::PDFDocument aDocument;
+ load(u"PDFWithImages.pdf", aDocument);
+
+ // Assert that the XObject in the page resources dictionary is a reference XObject.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+
+ // The document has 2 pages.
+ CPPUNIT_ASSERT_EQUAL(size_t(2), aPages.size());
+
+ // PAGE 1
+ {
+ vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
+ CPPUNIT_ASSERT(pResources);
+
+ auto pXObjects
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
+ CPPUNIT_ASSERT(pXObjects);
+
+ std::vector<OString> rIDs;
+ for (auto const& rPair : pXObjects->GetItems())
+ rIDs.push_back(rPair.first);
+
+ CPPUNIT_ASSERT_EQUAL(size_t(2), rIDs.size());
+
+ std::vector<int> aBitmapRefs1;
+ std::vector<int> aBitmapRefs2;
+
+ {
+ // FORM object 1
+ OString aID = rIDs[0];
+ CPPUNIT_ASSERT_EQUAL(OString("Im14"), aID);
+ vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(aID);
+ CPPUNIT_ASSERT(pXObject);
+
+ auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype->GetValue());
+
+ auto pInnerResources
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
+ CPPUNIT_ASSERT(pInnerResources);
+ auto pInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pInnerResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pInnerXObjects);
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects->GetItems().size());
+ OString aInnerObjectID = pInnerXObjects->GetItems().begin()->first;
+ CPPUNIT_ASSERT_EQUAL(OString("Im15"), aInnerObjectID);
+
+ vcl::filter::PDFObjectElement* pInnerXObject
+ = pInnerXObjects->LookupObject(aInnerObjectID);
+ CPPUNIT_ASSERT(pInnerXObject);
+
+ auto pInnerSubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerXObject->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pInnerSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pInnerSubtype->GetValue());
+
+ auto pInnerInnerResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pInnerXObject->Lookup("Resources"));
+ CPPUNIT_ASSERT(pInnerInnerResources);
+ auto pInnerInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pInnerInnerResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pInnerInnerXObjects);
+ CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects->GetItems().size());
+
+ std::vector<OString> aBitmapIDs1;
+ for (auto const& rPair : pInnerInnerXObjects->GetItems())
+ aBitmapIDs1.push_back(rPair.first);
+
+ {
+ CPPUNIT_ASSERT_EQUAL(OString("Im11"), aBitmapIDs1[0]);
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
+ pInnerInnerXObjects->LookupElement(aBitmapIDs1[0]));
+ CPPUNIT_ASSERT(pRef);
+ aBitmapRefs1.push_back(pRef->GetObjectValue());
+ CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
+
+ vcl::filter::PDFObjectElement* pBitmap
+ = pInnerInnerXObjects->LookupObject(aBitmapIDs1[0]);
+ CPPUNIT_ASSERT(pBitmap);
+ auto pBitmapSubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pBitmapSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
+ }
+ {
+ CPPUNIT_ASSERT_EQUAL(OString("Im5"), aBitmapIDs1[1]);
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
+ pInnerInnerXObjects->LookupElement(aBitmapIDs1[1]));
+ CPPUNIT_ASSERT(pRef);
+ aBitmapRefs1.push_back(pRef->GetObjectValue());
+ CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
+
+ vcl::filter::PDFObjectElement* pBitmap
+ = pInnerInnerXObjects->LookupObject(aBitmapIDs1[1]);
+ CPPUNIT_ASSERT(pBitmap);
+ auto pBitmapSubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pBitmapSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
+ }
+ }
+
+ {
+ // FORM object 2
+ OString aID = rIDs[1];
+ CPPUNIT_ASSERT_EQUAL(OString("Im5"), aID);
+ vcl::filter::PDFObjectElement* pXObject = pXObjects->LookupObject(aID);
+ CPPUNIT_ASSERT(pXObject);
+
+ auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pXObject->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pSubtype->GetValue());
+
+ auto pInnerResources
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pXObject->Lookup("Resources"));
+ CPPUNIT_ASSERT(pInnerResources);
+ auto pInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pInnerResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pInnerXObjects);
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects->GetItems().size());
+ OString aInnerObjectID = pInnerXObjects->GetItems().begin()->first;
+ CPPUNIT_ASSERT_EQUAL(OString("Im6"), aInnerObjectID);
+
+ vcl::filter::PDFObjectElement* pInnerXObject
+ = pInnerXObjects->LookupObject(aInnerObjectID);
+ CPPUNIT_ASSERT(pInnerXObject);
+
+ auto pInnerSubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerXObject->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pInnerSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pInnerSubtype->GetValue());
+
+ auto pInnerInnerResources = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pInnerXObject->Lookup("Resources"));
+ CPPUNIT_ASSERT(pInnerInnerResources);
+ auto pInnerInnerXObjects = dynamic_cast<vcl::filter::PDFDictionaryElement*>(
+ pInnerInnerResources->LookupElement("XObject"));
+ CPPUNIT_ASSERT(pInnerInnerXObjects);
+ CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects->GetItems().size());
+
+ std::vector<OString> aBitmapIDs2;
+ for (auto const& rPair : pInnerInnerXObjects->GetItems())
+ aBitmapIDs2.push_back(rPair.first);
+
+ {
+ CPPUNIT_ASSERT_EQUAL(OString("Im11"), aBitmapIDs2[0]);
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
+ pInnerInnerXObjects->LookupElement(aBitmapIDs2[0]));
+ CPPUNIT_ASSERT(pRef);
+ aBitmapRefs2.push_back(pRef->GetObjectValue());
+ CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
+
+ vcl::filter::PDFObjectElement* pBitmap
+ = pInnerInnerXObjects->LookupObject(aBitmapIDs2[0]);
+ CPPUNIT_ASSERT(pBitmap);
+ auto pBitmapSubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pBitmapSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
+ }
+ {
+ CPPUNIT_ASSERT_EQUAL(OString("Im5"), aBitmapIDs2[1]);
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(
+ pInnerInnerXObjects->LookupElement(aBitmapIDs2[1]));
+ CPPUNIT_ASSERT(pRef);
+ aBitmapRefs2.push_back(pRef->GetObjectValue());
+ CPPUNIT_ASSERT_EQUAL(0, pRef->GetGenerationValue());
+
+ vcl::filter::PDFObjectElement* pBitmap
+ = pInnerInnerXObjects->LookupObject(aBitmapIDs2[1]);
+ CPPUNIT_ASSERT(pBitmap);
+ auto pBitmapSubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pBitmap->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pBitmapSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Image"), pBitmapSubtype->GetValue());
+ }
+ }
+ // Ref should point to the same bitmap
+ CPPUNIT_ASSERT_EQUAL(aBitmapRefs1[0], aBitmapRefs2[0]);
+ CPPUNIT_ASSERT_EQUAL(aBitmapRefs1[1], aBitmapRefs2[1]);
+ }
+
+#endif
+}
+
+// Check we correctly copy more complex resources (Fonts describing
+// glyphs in recursive arrays) to the target PDF
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testReexportDocumentWithComplexResources)
+{
+// setenv only works on unix based systems
+#ifndef _WIN32
+ // We need to enable PDFium import (and make sure to disable after the test)
+ bool bResetEnvVar = false;
+ if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+ {
+ bResetEnvVar = true;
+ setenv("LO_IMPORT_USE_PDFIUM", "1", false);
+ }
+ comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+ if (bResetEnvVar)
+ unsetenv("LO_IMPORT_USE_PDFIUM");
+ });
+
+ // Load the PDF and save as PDF
+ vcl::filter::PDFDocument aDocument;
+ load(u"ComplexContentDictionary.pdf", aDocument);
+
+ // Assert that the XObject in the page resources dictionary is a reference XObject.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), aPages.size());
+
+ // Go directly to the Font object (24 0) (number could change if we change how PDF export works)
+ auto pFont = aDocument.LookupObject(24);
+ CPPUNIT_ASSERT(pFont);
+
+ // Check it is the Font object (Type = Font)
+ auto pName
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pFont->GetDictionary()->LookupElement("Type"));
+ CPPUNIT_ASSERT(pName);
+ CPPUNIT_ASSERT_EQUAL(OString("Font"), pName->GetValue());
+
+ // Check BaseFont is what we expect
+ auto pBaseFont = dynamic_cast<vcl::filter::PDFNameElement*>(
+ pFont->GetDictionary()->LookupElement("BaseFont"));
+ CPPUNIT_ASSERT(pBaseFont);
+ CPPUNIT_ASSERT_EQUAL(OString("HOTOMR+Calibri,Italic"), pBaseFont->GetValue());
+
+ // Check and get the W array
+ auto pWArray
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pFont->GetDictionary()->LookupElement("W"));
+ CPPUNIT_ASSERT(pWArray);
+ CPPUNIT_ASSERT_EQUAL(size_t(26), pWArray->GetElements().size());
+
+ // Check the content of W array
+ // ObjectCopier didn't copy this array correctly and the document
+ // had glyphs at the wrong places
+ {
+ // first 2 elements
+ auto pNumberAtIndex0 = dynamic_cast<vcl::filter::PDFNumberElement*>(pWArray->GetElement(0));
+ CPPUNIT_ASSERT(pNumberAtIndex0);
+ CPPUNIT_ASSERT_EQUAL(3.0, pNumberAtIndex0->GetValue());
+
+ auto pArrayAtIndex1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pWArray->GetElement(1));
+ CPPUNIT_ASSERT(pArrayAtIndex1);
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pArrayAtIndex1->GetElements().size());
+
+ {
+ auto pNumber
+ = dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex1->GetElement(0));
+ CPPUNIT_ASSERT(pNumber);
+ CPPUNIT_ASSERT_EQUAL(226.0, pNumber->GetValue());
+ }
+
+ // last 2 elements
+ auto pNumberAtIndex24
+ = dynamic_cast<vcl::filter::PDFNumberElement*>(pWArray->GetElement(24));
+ CPPUNIT_ASSERT(pNumberAtIndex24);
+ CPPUNIT_ASSERT_EQUAL(894.0, pNumberAtIndex24->GetValue());
+
+ auto pArrayAtIndex25 = dynamic_cast<vcl::filter::PDFArrayElement*>(pWArray->GetElement(25));
+ CPPUNIT_ASSERT(pArrayAtIndex25);
+ CPPUNIT_ASSERT_EQUAL(size_t(2), pArrayAtIndex25->GetElements().size());
+
+ {
+ auto pNumber1
+ = dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex25->GetElement(0));
+ CPPUNIT_ASSERT(pNumber1);
+ CPPUNIT_ASSERT_EQUAL(303.0, pNumber1->GetValue());
+
+ auto pNumber2
+ = dynamic_cast<vcl::filter::PDFNumberElement*>(pArrayAtIndex25->GetElement(1));
+ CPPUNIT_ASSERT(pNumber2);
+ CPPUNIT_ASSERT_EQUAL(303.0, pNumber2->GetValue());
+ }
+ }
+#endif
+}
+
+// Tests that at export the PDF has the PDF/UA metadata properly set
+// when we enable PDF/UA support.
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfUaMetadata)
+{
+ // Import a basic document (document doesn't really matter)
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"BrownFoxLazyDog.odt");
+
+ // Parse the export result.
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ auto* pCatalog = aDocument.GetCatalog();
+ CPPUNIT_ASSERT(pCatalog);
+ auto* pCatalogDictionary = pCatalog->GetDictionary();
+ CPPUNIT_ASSERT(pCatalogDictionary);
+ auto* pMetadataObject = pCatalogDictionary->LookupObject("Metadata");
+ CPPUNIT_ASSERT(pMetadataObject);
+ auto* pMetadataDictionary = pMetadataObject->GetDictionary();
+ auto* pType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pMetadataDictionary->LookupElement("Type"));
+ CPPUNIT_ASSERT(pType);
+ CPPUNIT_ASSERT_EQUAL(OString("Metadata"), pType->GetValue());
+
+ auto* pStreamObject = pMetadataObject->GetStream();
+ CPPUNIT_ASSERT(pStreamObject);
+ auto& rStream = pStreamObject->GetMemory();
+ rStream.Seek(0);
+
+ // Search for the PDF/UA marker in the metadata
+
+ tools::XmlWalker aWalker;
+ CPPUNIT_ASSERT(aWalker.open(&rStream));
+ CPPUNIT_ASSERT_EQUAL(OString("xmpmeta"), aWalker.name());
+
+ bool bPdfUaMarkerFound = false;
+ OString aPdfUaPart;
+
+ aWalker.children();
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "RDF"
+ && aWalker.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
+ {
+ aWalker.children();
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "Description"
+ && aWalker.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
+ {
+ aWalker.children();
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "part"
+ && aWalker.namespaceHref() == "http://www.aiim.org/pdfua/ns/id/")
+ {
+ aPdfUaPart = aWalker.content();
+ bPdfUaMarkerFound = true;
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+
+ CPPUNIT_ASSERT(bPdfUaMarkerFound);
+ CPPUNIT_ASSERT_EQUAL(OString("1"), aPdfUaPart);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf139736)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
+ { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"tdf139736-1.odt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
+ CPPUNIT_ASSERT(pContents);
+ vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ // Uncompress it.
+ SvMemoryStream aUncompressed;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ rObjectStream.Seek(0);
+ aZCodec.Decompress(rObjectStream, aUncompressed);
+ CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+ auto pStart = static_cast<const char*>(aUncompressed.GetData());
+ const char* const pEnd = pStart + aUncompressed.GetSize();
+
+ enum
+ {
+ Default,
+ Artifact,
+ ArtifactProps1,
+ ArtifactProps2,
+ Tagged
+ } state
+ = Default;
+
+ auto nLine(0);
+ auto nTagged(0);
+ auto nArtifacts(0);
+ while (true)
+ {
+ ++nLine;
+ auto const pLine = ::std::find(pStart, pEnd, '\n');
+ if (pLine == pEnd)
+ {
+ break;
+ }
+ std::string_view const line(pStart, pLine - pStart);
+ pStart = pLine + 1;
+ if (!line.empty() && line[0] != '%')
+ {
+ ::std::cerr << nLine << ": " << line << "\n";
+ if (line == "/Artifact BMC")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Artifact;
+ ++nArtifacts;
+ }
+ else if (o3tl::starts_with(line, "/Artifact <<"))
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ // check header/footer properties
+ CPPUNIT_ASSERT_EQUAL(std::string_view("/Type/Pagination"), line.substr(12));
+ state = ArtifactProps1;
+ ++nArtifacts;
+ }
+ else if (state == ArtifactProps1)
+ {
+ CPPUNIT_ASSERT_EQUAL(std::string_view("/Subtype/Header"), line);
+ state = ArtifactProps2;
+ }
+ else if (state == ArtifactProps2 && line == ">> BDC")
+ {
+ state = Artifact;
+ }
+ else if (line == "/Standard<</MCID 0>>BDC")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Tagged;
+ ++nTagged;
+ }
+ else if (line == "EMC")
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
+ state = Default;
+ }
+ else if (nLine > 1) // first line is expected "0.1 w"
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged)>(1), nTagged); // text in body
+ // 1 image and 1 frame and 1 header text; arbitrary number of aux stuff like borders
+ CPPUNIT_ASSERT(nArtifacts >= 3);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf152231)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
+ { "ExportNotesInMargin", uno::Any(true) },
+ { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"tdf152231.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
+ CPPUNIT_ASSERT(pContents);
+ vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ // Uncompress it.
+ SvMemoryStream aUncompressed;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ rObjectStream.Seek(0);
+ aZCodec.Decompress(rObjectStream, aUncompressed);
+ CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+ auto pStart = static_cast<const char*>(aUncompressed.GetData());
+ const char* const pEnd = pStart + aUncompressed.GetSize();
+
+ enum
+ {
+ Default,
+ Artifact,
+ Tagged
+ } state
+ = Default;
+
+ auto nLine(0);
+ auto nTagged(0);
+ auto nArtifacts(0);
+ while (true)
+ {
+ ++nLine;
+ auto const pLine = ::std::find(pStart, pEnd, '\n');
+ if (pLine == pEnd)
+ {
+ break;
+ }
+ std::string_view const line(pStart, pLine - pStart);
+ pStart = pLine + 1;
+ if (!line.empty() && line[0] != '%')
+ {
+ ::std::cerr << nLine << ": " << line << "\n";
+ if (line == "/Artifact BMC")
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Artifact;
+ ++nArtifacts;
+ }
+ else if (o3tl::starts_with(line, "/Standard<</MCID "))
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Tagged;
+ ++nTagged;
+ }
+ else if (line == "EMC")
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
+ state = Default;
+ }
+ else if (nLine > 1) // first line is expected "0.1 w"
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
+ CPPUNIT_ASSERT(nTagged >= 12); // text in body
+ // 1 annotation
+ CPPUNIT_ASSERT(nArtifacts >= 1);
+
+ auto nPara(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject)
+ continue;
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ if (pType && pType->GetValue() == "StructElem")
+ {
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+ if (pS && pS->GetValue() == "Standard")
+ {
+ ++nPara;
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject->Lookup("K"));
+ CPPUNIT_ASSERT(pKids);
+ // one problem was that some StructElem were missing kids
+ CPPUNIT_ASSERT(!pKids->GetElements().empty());
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nPara)>(12), nPara);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf152235)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
+ { { "PDFUACompliance", uno::Any(true) },
+ { "Watermark", uno::Any(OUString("kendy")) },
+ // need to set a font to avoid assertions about missing "Helvetica"
+ { "WatermarkFontName", uno::Any(OUString("Liberation Sans")) },
+ { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ mxComponent = loadFromDesktop("private:factory/swriter");
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
+ CPPUNIT_ASSERT(pContents);
+ vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ // Uncompress it.
+ SvMemoryStream aUncompressed;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ rObjectStream.Seek(0);
+ aZCodec.Decompress(rObjectStream, aUncompressed);
+ CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+ auto pStart = static_cast<const char*>(aUncompressed.GetData());
+ const char* const pEnd = pStart + aUncompressed.GetSize();
+
+ enum
+ {
+ Default,
+ Artifact,
+ Tagged
+ } state
+ = Default;
+
+ auto nLine(0);
+ auto nTagged(0);
+ auto nArtifacts(0);
+ while (true)
+ {
+ ++nLine;
+ auto const pLine = ::std::find(pStart, pEnd, '\n');
+ if (pLine == pEnd)
+ {
+ break;
+ }
+ std::string_view const line(pStart, pLine - pStart);
+ pStart = pLine + 1;
+ if (!line.empty() && line[0] != '%')
+ {
+ ::std::cerr << nLine << ": " << line << "\n";
+ if (o3tl::starts_with(line, "/Artifact "))
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Artifact;
+ ++nArtifacts;
+ }
+ else if (o3tl::starts_with(line, "/Standard<</MCID "))
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Tagged;
+ ++nTagged;
+ }
+ else if (line == "EMC")
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
+ state = Default;
+ }
+ else if (nLine > 1) // first line is expected "0.1 w"
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
+ CPPUNIT_ASSERT(nTagged >= 0); // text in body
+ CPPUNIT_ASSERT(nArtifacts >= 2); // 1 watermark + 1 other thing
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf149140)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"TableTH_test_LibreOfficeWriter7.3.3_HeaderRow-HeadersInTopRow.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ int nTH(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject)
+ continue;
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ if (pType && pType->GetValue() == "StructElem")
+ {
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+ if (pS && pS->GetValue() == "TH")
+ {
+ int nTable(0);
+ auto pAttrs = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject->Lookup("A"));
+ CPPUNIT_ASSERT(pAttrs != nullptr);
+ for (const auto& rAttrRef : pAttrs->GetElements())
+ {
+ auto pAttrDict = dynamic_cast<vcl::filter::PDFDictionaryElement*>(rAttrRef);
+ CPPUNIT_ASSERT(pAttrDict != nullptr);
+ auto pOwner
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAttrDict->LookupElement("O"));
+ CPPUNIT_ASSERT(pOwner != nullptr);
+ if (pOwner->GetValue() == "Table")
+ {
+ auto pScope = dynamic_cast<vcl::filter::PDFNameElement*>(
+ pAttrDict->LookupElement("Scope"));
+ CPPUNIT_ASSERT(pScope != nullptr);
+ CPPUNIT_ASSERT_EQUAL(OString("Column"), pScope->GetValue());
+ ++nTable;
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(1), nTable);
+ ++nTH;
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(6), nTH);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testNestedSection)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"nestedsection.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // the assert needs 2 follows to reproduce => 3 pages
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages.size());
+
+ auto nDoc(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructElem")
+ {
+ auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
+ if (pS1 && pS1->GetValue() == "Document")
+ {
+ auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1);
+ // assume there are no MCID ref at this level
+ auto pKids1v = pKids1->GetElements();
+ auto pRefKid10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[0]);
+ CPPUNIT_ASSERT(pRefKid10);
+ auto pObject10 = pRefKid10->LookupObject();
+ CPPUNIT_ASSERT(pObject10);
+ auto pType10
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10->GetValue());
+ auto pS10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Sect"), pS10->GetValue());
+
+ auto pKids10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10->Lookup("K"));
+ CPPUNIT_ASSERT(pKids10);
+ // assume there are no MCID ref at this level
+ auto pKids10v = pKids10->GetElements();
+
+ auto pRefKid100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[0]);
+ CPPUNIT_ASSERT(pRefKid100);
+ auto pObject100 = pRefKid100->LookupObject();
+ CPPUNIT_ASSERT(pObject100);
+ auto pType100
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100->GetValue());
+ auto pS100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS100->GetValue());
+
+ auto pRefKid101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[1]);
+ CPPUNIT_ASSERT(pRefKid101);
+ auto pObject101 = pRefKid101->LookupObject();
+ CPPUNIT_ASSERT(pObject101);
+ auto pType101
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType101->GetValue());
+ auto pS101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS101->GetValue());
+
+ auto pRefKid102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[2]);
+ CPPUNIT_ASSERT(pRefKid102);
+ auto pObject102 = pRefKid102->LookupObject();
+ CPPUNIT_ASSERT(pObject102);
+ auto pType102
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102->GetValue());
+ auto pS102 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Sect"), pS102->GetValue());
+
+ auto pKids102
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102->Lookup("K"));
+ CPPUNIT_ASSERT(pKids102);
+ // assume there are no MCID ref at this level
+ auto pKids102v = pKids102->GetElements();
+
+ auto pRefKid1020 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids102v[0]);
+ CPPUNIT_ASSERT(pRefKid1020);
+ auto pObject1020 = pRefKid1020->LookupObject();
+ CPPUNIT_ASSERT(pObject1020);
+ auto pType1020
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1020->GetValue());
+ auto pS1020 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS1020->GetValue());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pKids102v.size());
+
+ auto pRefKid103 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[3]);
+ CPPUNIT_ASSERT(pRefKid103);
+ auto pObject103 = pRefKid103->LookupObject();
+ CPPUNIT_ASSERT(pObject103);
+ auto pType103
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103->GetValue());
+ auto pS103 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS103->GetValue());
+
+ auto pRefKid104 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids10v[4]);
+ CPPUNIT_ASSERT(pRefKid104);
+ auto pObject104 = pRefKid104->LookupObject();
+ CPPUNIT_ASSERT(pObject104);
+ auto pType104
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject104->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType104->GetValue());
+ auto pS104 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject104->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS104->GetValue());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(5), pKids10v.size());
+
+ auto pRefKid11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[1]);
+ CPPUNIT_ASSERT(pRefKid11);
+ auto pObject11 = pRefKid11->LookupObject();
+ CPPUNIT_ASSERT(pObject11);
+ auto pType11
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11->GetValue());
+ auto pS11 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS11->GetValue());
+
+ CPPUNIT_ASSERT_EQUAL(size_t(2), pKids1v.size());
+ ++nDoc;
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDoc)>(1), nDoc);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157817)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"SimpleTOC.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPages.size());
+
+ vcl::filter::PDFObjectElement* pTOC(nullptr);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructElem")
+ {
+ auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
+ if (pS1 && pS1->GetValue() == "TOC")
+ {
+ pTOC = pObject1;
+ }
+ }
+ }
+ CPPUNIT_ASSERT(pTOC);
+
+ auto pKidsT = dynamic_cast<vcl::filter::PDFArrayElement*>(pTOC->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT);
+ // assume there are no MCID ref at this level
+ auto pKidsTv = pKidsT->GetElements();
+ auto pRefKidT0 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[0]);
+ CPPUNIT_ASSERT(pRefKidT0);
+ auto pObjectT0 = pRefKidT0->LookupObject();
+ CPPUNIT_ASSERT(pObjectT0);
+ auto pTypeT0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT0->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT0->GetValue());
+ auto pST0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT0->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Caption"), pST0->GetValue());
+
+ auto pKidsT0 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT0->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT0);
+ auto pKidsT0v = pKidsT0->GetElements();
+ auto pRefKidT00 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT0v[0]);
+ CPPUNIT_ASSERT(pRefKidT00);
+ auto pObjectT00 = pRefKidT00->LookupObject();
+ CPPUNIT_ASSERT(pObjectT00);
+ auto pTypeT00 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT00->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT00->GetValue());
+ auto pST00 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT00->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Contents#20Heading"), pST00->GetValue());
+
+ auto pRefKidT1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
+ CPPUNIT_ASSERT(pRefKidT1);
+ auto pObjectT1 = pRefKidT1->LookupObject();
+ CPPUNIT_ASSERT(pObjectT1);
+ auto pTypeT1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT1->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT1->GetValue());
+ auto pST1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT1->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST1->GetValue());
+
+ auto pKidsT1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT1->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT1);
+ auto pKidsT1v = pKidsT1->GetElements();
+
+ auto pRefKidT10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT1v[0]);
+ CPPUNIT_ASSERT(pRefKidT10);
+ auto pObjectT10 = pRefKidT10->LookupObject();
+ CPPUNIT_ASSERT(pObjectT10);
+ auto pTypeT10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT10->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT10->GetValue());
+ auto pST10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT10->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST10->GetValue());
+
+ auto pKidsT10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT10->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT10);
+ auto pKidsT10v = pKidsT10->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT10v.size());
+
+ // there is one and only one Link
+ auto pRefKidT100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT10v[0]);
+ CPPUNIT_ASSERT(pRefKidT100);
+ auto pObjectT100 = pRefKidT100->LookupObject();
+ CPPUNIT_ASSERT(pObjectT100);
+ auto pTypeT100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT100->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT100->GetValue());
+ auto pST100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT100->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pST100->GetValue());
+
+ auto pRefKidT2 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
+ CPPUNIT_ASSERT(pRefKidT2);
+ auto pObjectT2 = pRefKidT2->LookupObject();
+ CPPUNIT_ASSERT(pObjectT2);
+ auto pTypeT2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT2->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT2->GetValue());
+ auto pST2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT2->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST2->GetValue());
+
+ auto pKidsT2 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT2->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT2);
+ auto pKidsT2v = pKidsT2->GetElements();
+
+ auto pRefKidT20 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT2v[0]);
+ CPPUNIT_ASSERT(pRefKidT20);
+ auto pObjectT20 = pRefKidT20->LookupObject();
+ CPPUNIT_ASSERT(pObjectT20);
+ auto pTypeT20 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT20->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT20->GetValue());
+ auto pST20 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT20->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST20->GetValue());
+
+ auto pKidsT20 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT20->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT20);
+ auto pKidsT20v = pKidsT20->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT20v.size());
+
+ // there is one and only one Link
+ auto pRefKidT200 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT20v[0]);
+ CPPUNIT_ASSERT(pRefKidT200);
+ auto pObjectT200 = pRefKidT200->LookupObject();
+ CPPUNIT_ASSERT(pObjectT200);
+ auto pTypeT200 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT200->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT200->GetValue());
+ auto pST200 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT200->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pST200->GetValue());
+
+ auto pRefKidT3 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsTv[1]);
+ CPPUNIT_ASSERT(pRefKidT3);
+ auto pObjectT3 = pRefKidT3->LookupObject();
+ CPPUNIT_ASSERT(pObjectT3);
+ auto pTypeT3 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT3->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT3->GetValue());
+ auto pST3 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT3->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("TOCI"), pST3->GetValue());
+
+ auto pKidsT3 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT3->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT3);
+ auto pKidsT3v = pKidsT3->GetElements();
+
+ auto pRefKidT30 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT3v[0]);
+ CPPUNIT_ASSERT(pRefKidT30);
+ auto pObjectT30 = pRefKidT30->LookupObject();
+ CPPUNIT_ASSERT(pObjectT30);
+ auto pTypeT30 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT30->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT30->GetValue());
+ auto pST30 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT30->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Contents#201"), pST30->GetValue());
+
+ auto pKidsT30 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectT30->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsT30);
+ auto pKidsT30v = pKidsT30->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT30v.size());
+
+ // there is one and only one Link
+ auto pRefKidT300 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsT30v[0]);
+ CPPUNIT_ASSERT(pRefKidT300);
+ auto pObjectT300 = pRefKidT300->LookupObject();
+ CPPUNIT_ASSERT(pObjectT300);
+ auto pTypeT300 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT300->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeT300->GetValue());
+ auto pST300 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectT300->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pST300->GetValue());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf135638)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"image-shape.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ int nFigure(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject)
+ continue;
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ if (pType && pType->GetValue() == "StructElem")
+ {
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+ if (pS && pS->GetValue() == "Figure")
+ {
+ auto pAttrDict
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject->Lookup("A"));
+ CPPUNIT_ASSERT(pAttrDict != nullptr);
+ auto pOwner
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAttrDict->LookupElement("O"));
+ CPPUNIT_ASSERT(pOwner != nullptr);
+ CPPUNIT_ASSERT_EQUAL(OString("Layout"), pOwner->GetValue());
+ auto pBBox
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pAttrDict->LookupElement("BBox"));
+ CPPUNIT_ASSERT(pBBox != nullptr);
+ if (nFigure == 0)
+ {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 139.5,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[0])
+ ->GetValue(),
+ 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 480.3,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[1])
+ ->GetValue(),
+ 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 472.5,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[2])
+ ->GetValue(),
+ 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 735.3,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[3])
+ ->GetValue(),
+ 0.01);
+ }
+ else
+ {
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 178.45,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[0])
+ ->GetValue(),
+ 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 318.65,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[1])
+ ->GetValue(),
+ 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 326.35,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[2])
+ ->GetValue(),
+ 0.01);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(
+ 382.55,
+ dynamic_cast<vcl::filter::PDFNumberElement*>(pBBox->GetElements()[3])
+ ->GetValue(),
+ 0.01);
+ }
+ ++nFigure;
+ }
+ }
+ }
+ // the first one is a Writer image, 2nd one SdrRectObj
+ CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157703)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"LO_Lbl_Lbody_bug_report.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ vcl::filter::PDFObjectElement* pDocument(nullptr);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructElem")
+ {
+ auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
+ if (pS1 && pS1->GetValue() == "Document")
+ {
+ pDocument = pObject1;
+ }
+ }
+ }
+ CPPUNIT_ASSERT(pDocument);
+
+ auto pKidsD = dynamic_cast<vcl::filter::PDFArrayElement*>(pDocument->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsD);
+ // assume there are no MCID ref at this level
+ auto pKidsDv = pKidsD->GetElements();
+ auto pRefKidD0 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsDv[0]);
+ CPPUNIT_ASSERT(pRefKidD0);
+ auto pObjectD0 = pRefKidD0->LookupObject();
+ CPPUNIT_ASSERT(pObjectD0);
+ auto pTypeD0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD0->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeD0->GetValue());
+ auto pSD0 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD0->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("H1"), pSD0->GetValue());
+
+ auto pKidsD0 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectD0->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsD0);
+ auto pKidsD0v = pKidsD0->GetElements();
+ auto pRefKidD00 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD0v[0]);
+ // MCID for label
+ CPPUNIT_ASSERT(!pRefKidD00);
+
+ // MCID for text
+ auto pRefKidD01 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD0v[1]);
+ CPPUNIT_ASSERT(!pRefKidD01);
+
+ auto pRefKidD1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsDv[1]);
+ CPPUNIT_ASSERT(pRefKidD1);
+ auto pObjectD1 = pRefKidD1->LookupObject();
+ CPPUNIT_ASSERT(pObjectD1);
+ auto pTypeD1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD1->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pTypeD1->GetValue());
+ auto pSD1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObjectD1->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("H2"), pSD1->GetValue());
+
+ auto pKidsD1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObjectD1->Lookup("K"));
+ CPPUNIT_ASSERT(pKidsD1);
+ auto pKidsD1v = pKidsD1->GetElements();
+
+ // MCID for text
+ auto pRefKidD11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKidsD1v[0]);
+ CPPUNIT_ASSERT(!pRefKidD11);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testSpans)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"spanlist.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPages.size());
+
+ auto nDoc(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructElem")
+ {
+ auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
+ if (pS1 && pS1->GetValue() == "Document")
+ {
+ auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1);
+ // assume there are no MCID ref at this level
+ auto vKids1 = pKids1->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), vKids1.size());
+ auto pRefKid10 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1[0]);
+ CPPUNIT_ASSERT(pRefKid10);
+ auto pObject10 = pRefKid10->LookupObject();
+ CPPUNIT_ASSERT(pObject10);
+ auto pType10
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10->GetValue());
+ auto pS10 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("L"), pS10->GetValue());
+
+ auto pKids10 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10->Lookup("K"));
+ CPPUNIT_ASSERT(pKids10);
+ // assume there are no MCID ref at this level
+ auto vKids10 = pKids10->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(4), vKids10.size());
+
+ auto pRefKid100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[0]);
+ CPPUNIT_ASSERT(pRefKid100);
+ auto pObject100 = pRefKid100->LookupObject();
+ CPPUNIT_ASSERT(pObject100);
+ auto pType100
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100->GetValue());
+ auto pS100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LI"), pS100->GetValue());
+
+ auto pKids100
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject100->Lookup("K"));
+ CPPUNIT_ASSERT(pKids100);
+ // assume there are no MCID ref at this level
+ auto vKids100 = pKids100->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), vKids100.size());
+
+ auto pRefKid1000 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids100[0]);
+ CPPUNIT_ASSERT(pRefKid1000);
+ auto pObject1000 = pRefKid1000->LookupObject();
+ CPPUNIT_ASSERT(pObject1000);
+ auto pType1000
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1000->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1000->GetValue());
+ auto pS1000 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1000->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1000->GetValue());
+
+ auto pRefKid1001 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids100[1]);
+ CPPUNIT_ASSERT(pRefKid1001);
+ auto pObject1001 = pRefKid1001->LookupObject();
+ CPPUNIT_ASSERT(pObject1001);
+ auto pType1001
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1001->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1001->GetValue());
+ auto pS1001 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1001->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1001->GetValue());
+ auto pKids1001
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1001->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1001);
+ // assume there are no MCID ref at this level
+ auto vKids1001 = pKids1001->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1001.size());
+
+ auto pRefKid10010 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1001[0]);
+ CPPUNIT_ASSERT(pRefKid10010);
+ auto pObject10010 = pRefKid10010->LookupObject();
+ CPPUNIT_ASSERT(pObject10010);
+ auto pType10010
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10010->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10010->GetValue());
+ auto pS10010
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10010->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10010->GetValue());
+ auto pKids10010
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10010->Lookup("K"));
+ CPPUNIT_ASSERT(pKids10010);
+ // assume there are no MCID ref at this level
+ auto vKids10010 = pKids10010->GetElements();
+ // only one span
+ CPPUNIT_ASSERT_EQUAL(size_t(1), vKids10010.size());
+
+ auto pRefKid100100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10010[0]);
+ CPPUNIT_ASSERT(pRefKid100100);
+ auto pObject100100 = pRefKid100100->LookupObject();
+ CPPUNIT_ASSERT(pObject100100);
+ auto pType100100
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100100->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType100100->GetValue());
+ auto pS100100
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject100100->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Span"), pS100100->GetValue());
+ // this span exists because of lang
+ auto pLang100100 = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
+ pObject100100->Lookup("Lang"));
+ CPPUNIT_ASSERT_EQUAL(OString("en-GB"), pLang100100->GetValue());
+
+ auto pRefKid101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[1]);
+ CPPUNIT_ASSERT(pRefKid101);
+ auto pObject101 = pRefKid101->LookupObject();
+ CPPUNIT_ASSERT(pObject101);
+ auto pType101
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType101->GetValue());
+ auto pS101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject101->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LI"), pS101->GetValue());
+
+ auto pKids101
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject101->Lookup("K"));
+ CPPUNIT_ASSERT(pKids101);
+ // assume there are no MCID ref at this level
+ auto vKids101 = pKids101->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), vKids101.size());
+
+ auto pRefKid1010 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids101[0]);
+ CPPUNIT_ASSERT(pRefKid1010);
+ auto pObject1010 = pRefKid1010->LookupObject();
+ CPPUNIT_ASSERT(pObject1010);
+ auto pType1010
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1010->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1010->GetValue());
+ auto pS1010 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1010->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1010->GetValue());
+
+ auto pRefKid1011 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids101[1]);
+ CPPUNIT_ASSERT(pRefKid1011);
+ auto pObject1011 = pRefKid1011->LookupObject();
+ CPPUNIT_ASSERT(pObject1011);
+ auto pType1011
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1011->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1011->GetValue());
+ auto pS1011 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1011->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1011->GetValue());
+
+ auto pKids1011
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1011->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1011);
+ // assume there are no MCID ref at this level
+ auto vKids1011 = pKids1011->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1011.size());
+
+ auto pRefKid10110 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1011[0]);
+ CPPUNIT_ASSERT(pRefKid10110);
+ auto pObject10110 = pRefKid10110->LookupObject();
+ CPPUNIT_ASSERT(pObject10110);
+ auto pType10110
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10110->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10110->GetValue());
+ auto pS10110
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10110->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10110->GetValue());
+ auto pKids10110
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10110->Lookup("K"));
+ CPPUNIT_ASSERT(pKids10110);
+ auto vKids10110 = pKids10110->GetElements();
+ // only MCIDs, no span
+ for (size_t i = 0; i < vKids10110.size(); ++i)
+ {
+ auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10110[i]);
+ CPPUNIT_ASSERT(!pKid);
+ }
+
+ auto pRefKid102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[2]);
+ CPPUNIT_ASSERT(pRefKid102);
+ auto pObject102 = pRefKid102->LookupObject();
+ CPPUNIT_ASSERT(pObject102);
+ auto pType102
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102->GetValue());
+ auto pS102 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LI"), pS102->GetValue());
+
+ auto pKids102
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102->Lookup("K"));
+ CPPUNIT_ASSERT(pKids102);
+ // assume there are no MCID ref at this level
+ auto vKids102 = pKids102->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), vKids102.size());
+
+ auto pRefKid1020 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102[0]);
+ CPPUNIT_ASSERT(pRefKid1020);
+ auto pObject1020 = pRefKid1020->LookupObject();
+ CPPUNIT_ASSERT(pObject1020);
+ auto pType1020
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1020->GetValue());
+ auto pS1020 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1020->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1020->GetValue());
+
+ auto pRefKid1021 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102[1]);
+ CPPUNIT_ASSERT(pRefKid1021);
+ auto pObject1021 = pRefKid1021->LookupObject();
+ CPPUNIT_ASSERT(pObject1021);
+ auto pType1021
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1021->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1021->GetValue());
+ auto pS1021 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1021->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1021->GetValue());
+
+ auto pKids1021
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1021->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1021);
+ // assume there are no MCID ref at this level
+ auto vKids1021 = pKids1021->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1021.size());
+
+ auto pRefKid10210 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1021[0]);
+ CPPUNIT_ASSERT(pRefKid10210);
+ auto pObject10210 = pRefKid10210->LookupObject();
+ CPPUNIT_ASSERT(pObject10210);
+ auto pType10210
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10210->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10210->GetValue());
+ auto pS10210
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10210->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10210->GetValue());
+ auto pKids10210
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10210->Lookup("K"));
+ CPPUNIT_ASSERT(pKids10210);
+ // assume there are no MCID ref at this level
+ auto vKids10210 = pKids10210->GetElements();
+ // 2 span and a hyperlink
+ CPPUNIT_ASSERT_EQUAL(size_t(3), vKids10210.size());
+
+ auto pRefKid102100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[0]);
+ CPPUNIT_ASSERT(pRefKid102100);
+ auto pObject102100 = pRefKid102100->LookupObject();
+ CPPUNIT_ASSERT(pObject102100);
+ auto pType102100
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102100->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102100->GetValue());
+ auto pS102100
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102100->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Span"), pS102100->GetValue());
+ auto pKids102100
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102100->Lookup("K"));
+ CPPUNIT_ASSERT(pKids102100);
+ auto vKids102100 = pKids102100->GetElements();
+ for (size_t i = 0; i < vKids102100.size(); ++i)
+ {
+ auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102100[i]);
+ CPPUNIT_ASSERT(!pKid);
+ }
+
+ auto pRefKid102101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[1]);
+ CPPUNIT_ASSERT(pRefKid102101);
+ auto pObject102101 = pRefKid102101->LookupObject();
+ CPPUNIT_ASSERT(pObject102101);
+ auto pType102101
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102101->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102101->GetValue());
+ auto pS102101
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102101->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pS102101->GetValue());
+ auto pKids102101
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102101->Lookup("K"));
+ CPPUNIT_ASSERT(pKids102101);
+ auto vKids102101 = pKids102101->GetElements();
+ auto nRef(0);
+ for (size_t i = 0; i < vKids102101.size(); ++i)
+ {
+ auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102101[i]);
+ if (pKid)
+ {
+ ++nRef; // annotation
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+
+ auto pRefKid102102 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10210[2]);
+ CPPUNIT_ASSERT(pRefKid102102);
+ auto pObject102102 = pRefKid102102->LookupObject();
+ CPPUNIT_ASSERT(pObject102102);
+ auto pType102102
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102102->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType102102->GetValue());
+ auto pS102102
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject102102->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Span"), pS102102->GetValue());
+ auto pKids102102
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject102102->Lookup("K"));
+ CPPUNIT_ASSERT(pKids102102);
+ auto vKids102102 = pKids102102->GetElements();
+ // there is a footnote
+ auto nFtn(0);
+ for (size_t i = 0; i < vKids102102.size(); ++i)
+ {
+ auto pKid = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids102102[i]);
+ if (pKid)
+ {
+ auto pObject = pKid->LookupObject();
+ CPPUNIT_ASSERT(pObject);
+ auto pType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pS->GetValue());
+ ++nFtn;
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFtn)>(1), nFtn);
+
+ auto pRefKid103 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10[3]);
+ CPPUNIT_ASSERT(pRefKid103);
+ auto pObject103 = pRefKid103->LookupObject();
+ CPPUNIT_ASSERT(pObject103);
+ auto pType103
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103->GetValue());
+ auto pS103 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LI"), pS103->GetValue());
+
+ auto pKids103
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject103->Lookup("K"));
+ CPPUNIT_ASSERT(pKids103);
+ // assume there are no MCID ref at this level
+ auto vKids103 = pKids103->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), vKids103.size());
+
+ auto pRefKid1030 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids103[0]);
+ CPPUNIT_ASSERT(pRefKid1030);
+ auto pObject1030 = pRefKid1030->LookupObject();
+ CPPUNIT_ASSERT(pObject1030);
+ auto pType1030
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1030->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1030->GetValue());
+ auto pS1030 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1030->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1030->GetValue());
+
+ auto pRefKid1031 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids103[1]);
+ CPPUNIT_ASSERT(pRefKid1031);
+ auto pObject1031 = pRefKid1031->LookupObject();
+ CPPUNIT_ASSERT(pObject1031);
+ auto pType1031
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1031->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1031->GetValue());
+ auto pS1031 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1031->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("LBody"), pS1031->GetValue());
+
+ auto pKids1031
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1031->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1031);
+ // assume there are no MCID ref at this level
+ auto vKids1031 = pKids1031->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1031.size());
+
+ auto pRefKid10310 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1031[0]);
+ CPPUNIT_ASSERT(pRefKid10310);
+ auto pObject10310 = pRefKid10310->LookupObject();
+ CPPUNIT_ASSERT(pObject10310);
+ auto pType10310
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10310->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType10310->GetValue());
+ auto pS10310
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject10310->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pS10310->GetValue());
+ auto pKids10310
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject10310->Lookup("K"));
+ CPPUNIT_ASSERT(pKids10310);
+ // assume there are no MCID ref at this level
+ auto vKids10310 = pKids10310->GetElements();
+ // only one span, following a MCID for some strike-out gap
+ CPPUNIT_ASSERT_EQUAL(size_t(2), vKids10310.size());
+
+ auto pRefKid103100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10310[0]);
+ CPPUNIT_ASSERT(!pRefKid103100);
+
+ auto pRefKid103101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids10310[1]);
+ CPPUNIT_ASSERT(pRefKid103101);
+ auto pObject103101 = pRefKid103101->LookupObject();
+ CPPUNIT_ASSERT(pObject103101);
+ auto pType103101
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103101->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType103101->GetValue());
+ auto pS103101
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject103101->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Span"), pS103101->GetValue());
+ auto pDictA103101
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject103101->Lookup("A"));
+ CPPUNIT_ASSERT(pDictA103101 != nullptr);
+ CPPUNIT_ASSERT_EQUAL(OString("Layout"), dynamic_cast<vcl::filter::PDFNameElement*>(
+ pDictA103101->LookupElement("O"))
+ ->GetValue());
+ CPPUNIT_ASSERT_EQUAL(OString("LineThrough"),
+ dynamic_cast<vcl::filter::PDFNameElement*>(
+ pDictA103101->LookupElement("TextDecorationType"))
+ ->GetValue());
+
+ // now the footnote container - following the list
+ auto pRefKid11 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1[1]);
+ CPPUNIT_ASSERT(pRefKid11);
+ auto pObject11 = pRefKid11->LookupObject();
+ CPPUNIT_ASSERT(pObject11);
+ auto pType11
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11->GetValue());
+ auto pS11 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Div"), pS11->GetValue());
+
+ auto pKids11 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject11->Lookup("K"));
+ CPPUNIT_ASSERT(pKids11);
+ // assume there are no MCID ref at this level
+ auto vKids11 = pKids11->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), vKids11.size());
+
+ auto pRefKid110 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids11[0]);
+ CPPUNIT_ASSERT(pRefKid110);
+ auto pObject110 = pRefKid110->LookupObject();
+ CPPUNIT_ASSERT(pObject110);
+ auto pType110
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject110->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType110->GetValue());
+ auto pS110 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject110->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Note"), pS110->GetValue());
+
+ auto pKids110
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject110->Lookup("K"));
+ CPPUNIT_ASSERT(pKids110);
+ // assume there are no MCID ref at this level
+ auto vKids110 = pKids110->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(2), vKids110.size());
+
+ auto pRefKid1100 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids110[0]);
+ CPPUNIT_ASSERT(pRefKid1100);
+ auto pObject1100 = pRefKid1100->LookupObject();
+ CPPUNIT_ASSERT(pObject1100);
+ auto pType1100
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1100->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1100->GetValue());
+ auto pS1100 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1100->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Lbl"), pS1100->GetValue());
+
+ auto pKids1100
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1100->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1100);
+ // assume there are no MCID ref at this level
+ auto vKids1100 = pKids1100->GetElements();
+ CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1100.size());
+
+ auto pRefKid11000 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids1100[0]);
+ CPPUNIT_ASSERT(pRefKid11000);
+ auto pObject11000 = pRefKid11000->LookupObject();
+ CPPUNIT_ASSERT(pObject11000);
+ auto pType11000
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11000->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType11000->GetValue());
+ auto pS11000
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject11000->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pS11000->GetValue());
+
+ auto pRefKid1101 = dynamic_cast<vcl::filter::PDFReferenceElement*>(vKids110[1]);
+ CPPUNIT_ASSERT(pRefKid1101);
+ auto pObject1101 = pRefKid1101->LookupObject();
+ CPPUNIT_ASSERT(pObject1101);
+ auto pType1101
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1101->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType1101->GetValue());
+ auto pS1101 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1101->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Footnote"), pS1101->GetValue());
+
+ ++nDoc;
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDoc)>(1), nDoc);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157182)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({
+ { "PDFUACompliance", uno::Any(true) },
+ // only happens with PDF/A-1
+ { "SelectPdfVersion", uno::Any(static_cast<sal_Int32>(1)) },
+ }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+
+ saveAsPDF(u"transparentshape.fodp");
+
+ // just check this does not crash or assert
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf57423)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"Description PDF Export test .odt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ int nFigure(0);
+ int nFormula(0);
+ int nDiv(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject)
+ continue;
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ if (pType && pType->GetValue() == "StructElem")
+ {
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+ if (pS && pS->GetValue() == "Figure")
+ {
+ switch (nFigure)
+ {
+ case 2:
+ CPPUNIT_ASSERT_EQUAL(u"QR Code - Tells how to get to Mosegaard"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ case 0:
+ CPPUNIT_ASSERT_EQUAL(u"Title: Arrows - Description: Explains the "
+ u"different arrow appearances"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ case 1:
+ CPPUNIT_ASSERT_EQUAL(
+ u"My blue triangle - Does not need further description"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ }
+ ++nFigure;
+ }
+ if (pS && pS->GetValue() == "Formula")
+ {
+ CPPUNIT_ASSERT_EQUAL(
+ u"Equation 1 - Now we give the full description of eq 1 here"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(pObject->Lookup("Alt"))));
+ ++nFormula;
+ }
+ if (pS && pS->GetValue() == "Div")
+ {
+ switch (nDiv)
+ {
+ case 0:
+ CPPUNIT_ASSERT_EQUAL(u"This frame has a description"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ case 1:
+ // no properties set on this
+ CPPUNIT_ASSERT(!pObject->Lookup("Alt"));
+ break;
+ case 2:
+ CPPUNIT_ASSERT_EQUAL(u"My textbox - Has a light background"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ case 3:
+ CPPUNIT_ASSERT_EQUAL(u"Hey! There is no alternate text for Frame "
+ u"// but maybe not needed?"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ }
+ ++nDiv;
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(3), nFigure);
+ CPPUNIT_ASSERT_EQUAL(int(1), nFormula);
+ CPPUNIT_ASSERT_EQUAL(int(4), nDiv);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf154982)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"tdf154982.odt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ int nFigure(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject)
+ continue;
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ if (pType && pType->GetValue() == "StructElem")
+ {
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+ if (pS && pS->GetValue() == "Figure")
+ {
+ switch (nFigure)
+ {
+ case 0:
+ CPPUNIT_ASSERT_EQUAL(u"Here comes the signature - Please sign here"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ case 1:
+ CPPUNIT_ASSERT_EQUAL(u"Home"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ }
+
+ // the problem was that the figures in the hell layer were not
+ // below their anchor paragraphs in the structure tree
+ auto pParentRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject->Lookup("P"));
+ CPPUNIT_ASSERT(pParentRef);
+ auto pParent(pParentRef->LookupObject());
+ CPPUNIT_ASSERT(pParent);
+ auto pParentType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
+ auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pParentS->GetValue());
+
+ auto pPParentRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pParent->Lookup("P"));
+ CPPUNIT_ASSERT(pPParentRef);
+ auto pPParent(pPParentRef->LookupObject());
+ CPPUNIT_ASSERT(pPParent);
+ auto pPParentType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pPParentType->GetValue());
+ auto pPParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pPParent->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Document"), pPParentS->GetValue());
+ ++nFigure;
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf157397)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"PDF_export_with_formcontrol.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ vcl::filter::PDFObjectElement* pDocument(nullptr);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructElem")
+ {
+ auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
+ if (pS1 && pS1->GetValue() == "Document")
+ {
+ pDocument = pObject1;
+ }
+ }
+ }
+ CPPUNIT_ASSERT(pDocument);
+
+ auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pDocument->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1);
+ // assume there are no MCID ref at this level
+ auto pKids1v = pKids1->GetElements();
+ auto pRefKid12 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[2]);
+ CPPUNIT_ASSERT(pRefKid12);
+ auto pObject12 = pRefKid12->LookupObject();
+ CPPUNIT_ASSERT(pObject12);
+ auto pType12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType12->GetValue());
+ auto pS12 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject12->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS12->GetValue());
+
+ auto pKids12 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject12->Lookup("K"));
+ CPPUNIT_ASSERT(pKids12);
+ // assume there are no MCID ref at this level
+ auto pKids12v = pKids12->GetElements();
+ auto pRefKid120 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids12v[0]);
+ CPPUNIT_ASSERT(pRefKid120);
+ auto pObject120 = pRefKid120->LookupObject();
+ CPPUNIT_ASSERT(pObject120);
+ auto pType120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType120->GetValue());
+ auto pS120 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject120->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pS120->GetValue());
+
+ {
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject120->Lookup("K"));
+ auto nMCID(0);
+ auto nRef(0);
+ for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+ {
+ auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+ if (pNum)
+ {
+ ++nMCID;
+ }
+ if (pRef)
+ {
+ ++nRef;
+ auto pObjR = pRef->LookupObject();
+ auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+ auto pAnnotRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+ auto pAnnot = pAnnotRef->LookupObject();
+ auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
+ auto pASubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
+ auto pAContents
+ = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
+ CPPUNIT_ASSERT_EQUAL(
+ u"https://klexikon.zum.de/wiki/Kläranlage"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
+ auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
+ CPPUNIT_ASSERT(pAA);
+ auto pAAType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
+ auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
+ auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
+ pAA->LookupElement("URI"));
+ CPPUNIT_ASSERT_EQUAL(OString("https://klexikon.zum.de/wiki/Kl%C3%A4ranlage"),
+ pAAURI->GetValue());
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+ }
+
+ auto pRefKid13 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[3]);
+ CPPUNIT_ASSERT(pRefKid13);
+ auto pObject13 = pRefKid13->LookupObject();
+ CPPUNIT_ASSERT(pObject13);
+ auto pType13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType13->GetValue());
+ auto pS13 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject13->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS13->GetValue());
+
+ auto pKids13 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject13->Lookup("K"));
+ CPPUNIT_ASSERT(pKids13);
+ // assume there are no MCID ref at this level
+ auto pKids13v = pKids13->GetElements();
+ auto pRefKid130 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids13v[0]);
+ CPPUNIT_ASSERT(pRefKid130);
+ auto pObject130 = pRefKid130->LookupObject();
+ CPPUNIT_ASSERT(pObject130);
+ auto pType130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType130->GetValue());
+ auto pS130 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject130->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pS130->GetValue());
+
+ {
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject130->Lookup("K"));
+ auto nMCID(0);
+ auto nRef(0);
+ for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+ {
+ auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+ if (pNum)
+ {
+ ++nMCID;
+ }
+ if (pRef)
+ {
+ ++nRef;
+ auto pObjR = pRef->LookupObject();
+ auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+ auto pAnnotRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+ auto pAnnot = pAnnotRef->LookupObject();
+ auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
+ auto pASubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
+ auto pAContents
+ = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
+ CPPUNIT_ASSERT_EQUAL(
+ u"https://de.wikipedia.org/wiki/Kläranlage#Mechanische_Vorreinigung"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
+ auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
+ CPPUNIT_ASSERT(pAA);
+ auto pAAType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
+ auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
+ auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
+ pAA->LookupElement("URI"));
+ CPPUNIT_ASSERT_EQUAL(
+ OString(
+ "https://de.wikipedia.org/wiki/Kl%C3%A4ranlage#Mechanische_Vorreinigung"),
+ pAAURI->GetValue());
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+ }
+
+ auto pRefKid14 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[4]);
+ CPPUNIT_ASSERT(pRefKid14);
+ auto pObject14 = pRefKid14->LookupObject();
+ CPPUNIT_ASSERT(pObject14);
+ auto pType14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType14->GetValue());
+ auto pS14 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject14->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS14->GetValue());
+
+ auto pKids14 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject14->Lookup("K"));
+ CPPUNIT_ASSERT(pKids14);
+ // assume there are no MCID ref at this level
+ auto pKids14v = pKids14->GetElements();
+ auto pRefKid140 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids14v[0]);
+ CPPUNIT_ASSERT(pRefKid140);
+ auto pObject140 = pRefKid140->LookupObject();
+ CPPUNIT_ASSERT(pObject140);
+ auto pType140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType140->GetValue());
+ auto pS140 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject140->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pS140->GetValue());
+
+ {
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject140->Lookup("K"));
+ auto nMCID(0);
+ auto nRef(0);
+ for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+ {
+ auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+ if (pNum)
+ {
+ ++nMCID;
+ }
+ if (pRef)
+ {
+ ++nRef;
+ auto pObjR = pRef->LookupObject();
+ auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+ auto pAnnotRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+ auto pAnnot = pAnnotRef->LookupObject();
+ auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
+ auto pASubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
+ auto pAContents
+ = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
+ CPPUNIT_ASSERT_EQUAL(
+ u"https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pASubtype->GetValue());
+ auto pAA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
+ CPPUNIT_ASSERT(pAA);
+ auto pAAType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("Action"), pAAType->GetValue());
+ auto pAAS = dynamic_cast<vcl::filter::PDFNameElement*>(pAA->LookupElement("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("URI"), pAAS->GetValue());
+ auto pAAURI = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(
+ pAA->LookupElement("URI"));
+ CPPUNIT_ASSERT_EQUAL(
+ OString("https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"),
+ pAAURI->GetValue());
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+ }
+
+ auto pRefKid16 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids1v[6]);
+ CPPUNIT_ASSERT(pRefKid16);
+ auto pObject16 = pRefKid16->LookupObject();
+ CPPUNIT_ASSERT(pObject16);
+ auto pType16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType16->GetValue());
+ auto pS16 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject16->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Text#20body"), pS16->GetValue());
+
+ auto pKids16 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject16->Lookup("K"));
+ CPPUNIT_ASSERT(pKids16);
+ // assume there are no MCID ref at this level
+ auto pKids16v = pKids16->GetElements();
+ auto pRefKid160 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids16v[0]);
+ CPPUNIT_ASSERT(pRefKid160);
+ auto pObject160 = pRefKid160->LookupObject();
+ CPPUNIT_ASSERT(pObject160);
+ auto pType160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType160->GetValue());
+ auto pS160 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject160->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pS160->GetValue());
+ auto pA160Dict = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pObject160->Lookup("A"));
+ CPPUNIT_ASSERT(pA160Dict);
+ auto pA160O = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Dict->LookupElement("O"));
+ CPPUNIT_ASSERT(pA160O);
+ CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pA160O->GetValue());
+ auto pA160Role = dynamic_cast<vcl::filter::PDFNameElement*>(pA160Dict->LookupElement("Role"));
+ CPPUNIT_ASSERT(pA160Role);
+ CPPUNIT_ASSERT_EQUAL(OString("tv"), pA160Role->GetValue());
+
+ {
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject160->Lookup("K"));
+ auto nMCID(0);
+ auto nRef(0);
+ for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+ {
+ auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+ if (pNum)
+ {
+ ++nMCID;
+ }
+ if (pRef)
+ {
+ ++nRef;
+ auto pObjR = pRef->LookupObject();
+ auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+ auto pAnnotRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+ auto pAnnot = pAnnotRef->LookupObject();
+ auto pAType = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("Annot"), pAType->GetValue());
+ auto pASubtype
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"));
+ CPPUNIT_ASSERT_EQUAL(OString("Widget"), pASubtype->GetValue());
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf135192)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"tdf135192-1.fodp");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ int nTable(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructElem")
+ {
+ auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
+ if (pS1 && pS1->GetValue() == "Table")
+ {
+ int nTR(0);
+ auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1);
+ // there can be additional children, such as MCID ref
+ for (auto pKid1 : pKids1->GetElements())
+ {
+ auto pRefKid1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid1);
+ if (pRefKid1)
+ {
+ auto pObject2 = pRefKid1->LookupObject();
+ if (pObject2)
+ {
+ auto pType2 = dynamic_cast<vcl::filter::PDFNameElement*>(
+ pObject2->Lookup("Type"));
+ if (pType2 && pType2->GetValue() == "StructElem")
+ {
+ auto pS2 = dynamic_cast<vcl::filter::PDFNameElement*>(
+ pObject2->Lookup("S"));
+ if (pS2 && pS2->GetValue() == "TR")
+ {
+ int nTD(0);
+ auto pKids2 = dynamic_cast<vcl::filter::PDFArrayElement*>(
+ pObject2->Lookup("K"));
+ CPPUNIT_ASSERT(pKids2);
+ for (auto pKid2 : pKids2->GetElements())
+ {
+ auto pRefKid2
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(
+ pKid2);
+ if (pRefKid2)
+ {
+ auto pObject3 = pRefKid2->LookupObject();
+ if (pObject3)
+ {
+ auto pType3
+ = dynamic_cast<vcl::filter::PDFNameElement*>(
+ pObject3->Lookup("Type"));
+ if (pType3 && pType3->GetValue() == "StructElem")
+ {
+ auto pS3 = dynamic_cast<
+ vcl::filter::PDFNameElement*>(
+ pObject3->Lookup("S"));
+ if (nTR == 0 && pS3 && pS3->GetValue() == "TH")
+ {
+ int nOTable(0);
+ auto pAttrs = dynamic_cast<
+ vcl::filter::PDFArrayElement*>(
+ pObject3->Lookup("A"));
+ CPPUNIT_ASSERT(pAttrs != nullptr);
+ for (const auto& rAttrRef :
+ pAttrs->GetElements())
+ {
+ auto pAttrDict = dynamic_cast<
+ vcl::filter::PDFDictionaryElement*>(
+ rAttrRef);
+ CPPUNIT_ASSERT(pAttrDict != nullptr);
+ auto pOwner = dynamic_cast<
+ vcl::filter::PDFNameElement*>(
+ pAttrDict->LookupElement("O"));
+ CPPUNIT_ASSERT(pOwner != nullptr);
+ if (pOwner->GetValue() == "Table")
+ {
+ auto pScope = dynamic_cast<
+ vcl::filter::PDFNameElement*>(
+ pAttrDict->LookupElement(
+ "Scope"));
+ CPPUNIT_ASSERT(pScope != nullptr);
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Column"),
+ pScope->GetValue());
+ ++nOTable;
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(1), nOTable);
+ ++nTD;
+ }
+ else if (nTR != 0 && pS3
+ && pS3->GetValue() == "TD")
+ {
+ ++nTD;
+ }
+ }
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(3), nTD);
+ ++nTR;
+ }
+ }
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(2), nTR);
+ ++nTable;
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(int(1), nTable);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf154955)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ saveAsPDF(u"grouped-shape.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ vcl::filter::PDFObjectElement* pContents = aPages[0]->LookupObject("Contents");
+ CPPUNIT_ASSERT(pContents);
+ vcl::filter::PDFStreamElement* pStream = pContents->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ // Uncompress it.
+ SvMemoryStream aUncompressed;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ rObjectStream.Seek(0);
+ aZCodec.Decompress(rObjectStream, aUncompressed);
+ CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+ auto pStart = static_cast<const char*>(aUncompressed.GetData());
+ const char* const pEnd = pStart + aUncompressed.GetSize();
+
+ enum
+ {
+ Default,
+ Artifact,
+ Tagged
+ } state
+ = Default;
+
+ auto nLine(0);
+ auto nTagged(0);
+ auto nArtifacts(0);
+ while (true)
+ {
+ ++nLine;
+ auto const pLine = ::std::find(pStart, pEnd, '\n');
+ if (pLine == pEnd)
+ {
+ break;
+ }
+ std::string_view const line(pStart, pLine - pStart);
+ pStart = pLine + 1;
+ if (!line.empty() && line[0] != '%')
+ {
+ ::std::cerr << nLine << ": " << line << "\n";
+ if (o3tl::starts_with(line, "/Artifact "))
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Artifact;
+ ++nArtifacts;
+ }
+ else if (o3tl::starts_with(line, "/Figure<</MCID "))
+ {
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default, state);
+ state = Tagged;
+ ++nTagged;
+ }
+ else if (line == "EMC")
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
+ state = Default;
+ }
+ else if (nLine > 1) // first line is expected "0.1 w"
+ {
+ CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state != Default);
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default, state);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged)>(2), nTagged);
+ CPPUNIT_ASSERT(nArtifacts >= 1);
+
+ int nFigure(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject)
+ continue;
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ if (pType && pType->GetValue() == "StructElem")
+ {
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("S"));
+ if (pS && pS->GetValue() == "Figure")
+ {
+ switch (nFigure)
+ {
+ case 0:
+ CPPUNIT_ASSERT_EQUAL(u"Two rectangles - Grouped"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ case 1:
+ CPPUNIT_ASSERT_EQUAL(u"these ones are green"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject->Lookup("Alt"))));
+ break;
+ }
+
+ auto pParentRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject->Lookup("P"));
+ CPPUNIT_ASSERT(pParentRef);
+ auto pParent(pParentRef->LookupObject());
+ CPPUNIT_ASSERT(pParent);
+ auto pParentType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
+ auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Standard"), pParentS->GetValue());
+
+ ++nFigure;
+ }
+ }
+ }
+ // the problem was that there were 4 shapes (the sub-shapes of the 2 groups)
+ CPPUNIT_ASSERT_EQUAL(int(2), nFigure);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf155190)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+
+ saveAsPDF(u"tdf155190.odt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ auto nDiv(0);
+ auto nFigure(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+
+ auto pS1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("S"));
+ // start with the text box
+ if (pType1 && pType1->GetValue() == "StructElem" && pS1 && pS1->GetValue() == "Div")
+ {
+ ++nDiv;
+ auto pKids1 = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject1->Lookup("K"));
+ CPPUNIT_ASSERT(pKids1);
+ for (auto pKid1 : pKids1->GetElements())
+ {
+ auto pRefKid1 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid1);
+ if (pRefKid1)
+ {
+ auto pObject2 = pRefKid1->LookupObject();
+ CPPUNIT_ASSERT(pObject2);
+ auto pType2
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pObject2->Lookup("Type"));
+ CPPUNIT_ASSERT(pType2);
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType2->GetValue());
+ auto pS2 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject2->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("FigureCaption"), pS2->GetValue());
+ auto pKids2
+ = dynamic_cast<vcl::filter::PDFArrayElement*>(pObject2->Lookup("K"));
+ CPPUNIT_ASSERT(pKids2);
+ // there are additional children, MCID ref
+ for (auto pKid2 : pKids2->GetElements())
+ {
+ auto pRefKid2 = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKid2);
+ if (pRefKid2)
+ {
+ auto pObject3 = pRefKid2->LookupObject();
+ CPPUNIT_ASSERT(pObject3);
+ auto pType3 = dynamic_cast<vcl::filter::PDFNameElement*>(
+ pObject3->Lookup("Type"));
+ if (pType3 && pType3->GetValue() == "StructElem")
+ {
+ auto pS3 = dynamic_cast<vcl::filter::PDFNameElement*>(
+ pObject3->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Figure"), pS3->GetValue());
+ auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pObject3->Lookup("Alt"));
+ CPPUNIT_ASSERT_EQUAL(
+ OUString("Picture of apples"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
+ auto pKids3 = dynamic_cast<vcl::filter::PDFArrayElement*>(
+ pObject3->Lookup("K"));
+ CPPUNIT_ASSERT(pKids3);
+ // the problem was that this didn't reference an MCID
+ CPPUNIT_ASSERT(!pKids3->GetElements().empty());
+ ++nFigure;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDiv)>(1), nDiv);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nDiv)>(1), nFigure);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testMediaShapeAnnot)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+
+ saveAsPDF(u"vid.odt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
+ CPPUNIT_ASSERT(pAnnots);
+
+ // There should be one annotation
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
+ auto pAnnotReference
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
+ CPPUNIT_ASSERT(pAnnotReference);
+ // check /Annot - produced by sw
+ vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
+ CPPUNIT_ASSERT(pAnnot);
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Annot"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Screen"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
+
+ auto pA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
+ CPPUNIT_ASSERT(pA);
+ auto pR = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pA->LookupElement("R"));
+ CPPUNIT_ASSERT(pR);
+ auto pC = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pR->LookupElement("C"));
+ CPPUNIT_ASSERT(pC);
+ auto pCT = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pC->LookupElement("CT"));
+ CPPUNIT_ASSERT_EQUAL(OString("video/webm"), pCT->GetValue());
+ auto pD = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pC->LookupElement("D"));
+ CPPUNIT_ASSERT(pD);
+ auto pDesc = dynamic_cast<vcl::filter::PDFHexStringElement*>(pD->LookupElement("Desc"));
+ CPPUNIT_ASSERT(pDesc);
+ CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text\nand some description"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pDesc));
+ auto pAlt = dynamic_cast<vcl::filter::PDFArrayElement*>(pC->LookupElement("Alt"));
+ CPPUNIT_ASSERT(pAlt);
+ auto pLang = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAlt->GetElement(0));
+ CPPUNIT_ASSERT_EQUAL(OString(""), pLang->GetValue());
+ auto pAltText = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAlt->GetElement(1));
+ CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text\nand some description"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAltText));
+
+ auto pStructParent
+ = dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
+ CPPUNIT_ASSERT(pStructParent);
+
+ vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
+
+ // check ParentTree to find StructElem
+ auto nRoots(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructTreeRoot")
+ {
+ ++nRoots;
+ auto pParentTree
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
+ CPPUNIT_ASSERT(pParentTree);
+ auto pNumTree = pParentTree->LookupObject();
+ CPPUNIT_ASSERT(pNumTree);
+ auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
+ CPPUNIT_ASSERT(pNums);
+ auto nFound(0);
+ for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
+ {
+ auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
+ if (pI->GetValue() == pStructParent->GetValue())
+ {
+ ++nFound;
+ CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
+ pStructElemRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
+ CPPUNIT_ASSERT(pStructElemRef);
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
+
+ // check /StructElem - produced by drawinglayer
+ CPPUNIT_ASSERT(pStructElemRef);
+ auto pStructElem(pStructElemRef->LookupObject());
+ CPPUNIT_ASSERT(pStructElem);
+
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Annot"), pS->GetValue());
+ auto pSEAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
+ CPPUNIT_ASSERT_EQUAL(OUString("alternativloser text - and some description"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pSEAlt));
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
+ auto nMCID(0);
+ auto nRef(0);
+ for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+ {
+ auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+ if (pNum)
+ {
+ ++nMCID;
+ }
+ if (pRef)
+ {
+ ++nRef;
+ auto pObjR = pRef->LookupObject();
+ auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+ auto pAnnotRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+ CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFlyFrameHyperlinkAnnot)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+
+ saveAsPDF(u"image-hyperlink-alttext.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
+ CPPUNIT_ASSERT(pAnnots);
+
+ // There should be one annotation
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
+ auto pAnnotReference
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
+ CPPUNIT_ASSERT(pAnnotReference);
+ // check /Annot - produced by sw
+ vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
+ CPPUNIT_ASSERT(pAnnot);
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Annot"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Link"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
+
+ auto pContents = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Image2"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pContents));
+
+ auto pStructParent
+ = dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
+ CPPUNIT_ASSERT(pStructParent);
+
+ vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
+
+ // check ParentTree to find StructElem
+ auto nRoots(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructTreeRoot")
+ {
+ ++nRoots;
+ auto pParentTree
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
+ CPPUNIT_ASSERT(pParentTree);
+ auto pNumTree = pParentTree->LookupObject();
+ CPPUNIT_ASSERT(pNumTree);
+ auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
+ CPPUNIT_ASSERT(pNums);
+ auto nFound(0);
+ for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
+ {
+ auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
+ if (pI->GetValue() == pStructParent->GetValue())
+ {
+ ++nFound;
+ CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
+ pStructElemRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
+ CPPUNIT_ASSERT(pStructElemRef);
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
+
+ // check /StructElem - produced by sw painting code
+ CPPUNIT_ASSERT(pStructElemRef);
+ auto pStructElem(pStructElemRef->LookupObject());
+ CPPUNIT_ASSERT(pStructElem);
+
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Link"), pS->GetValue());
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
+ auto nMCID(0);
+ auto nRef(0);
+ for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+ {
+ auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+ if (pNum)
+ {
+ ++nMCID;
+ }
+ if (pRef)
+ {
+ ++nRef;
+ auto pObjR = pRef->LookupObject();
+ auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+ auto pAnnotRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+ CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+
+ // the Link is inside a Figure
+ auto pParentRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pStructElem->Lookup("P"));
+ CPPUNIT_ASSERT(pParentRef);
+ auto pParent(pParentRef->LookupObject());
+ CPPUNIT_ASSERT(pParent);
+ auto pParentType = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pParentType->GetValue());
+ auto pParentS = dynamic_cast<vcl::filter::PDFNameElement*>(pParent->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Figure"), pParentS->GetValue());
+ auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pParent->Lookup("Alt"));
+ CPPUNIT_ASSERT_EQUAL(OUString("Ship drawing - Very cute"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testFormControlAnnot)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable PDF/UA
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+
+ saveAsPDF(u"formcontrol.fodt");
+
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
+ CPPUNIT_ASSERT(pAnnots);
+
+ // There should be one annotation
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
+ auto pAnnotReference
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
+ CPPUNIT_ASSERT(pAnnotReference);
+ // check /Annot
+ vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
+ CPPUNIT_ASSERT(pAnnot);
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Annot"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Widget"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
+ auto pT = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAnnot->Lookup("T"));
+ CPPUNIT_ASSERT(pT);
+ CPPUNIT_ASSERT_EQUAL(OString("Check Box 1"), pT->GetValue());
+ auto pTU = dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("TU"));
+ CPPUNIT_ASSERT(pTU);
+ CPPUNIT_ASSERT_EQUAL(OUString("helpful text"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pTU));
+
+ auto pStructParent
+ = dynamic_cast<vcl::filter::PDFNumberElement*>(pAnnot->Lookup("StructParent"));
+ CPPUNIT_ASSERT(pStructParent);
+
+ vcl::filter::PDFReferenceElement* pStructElemRef(nullptr);
+
+ // check ParentTree to find StructElem
+ auto nRoots(0);
+ for (const auto& rDocElement : aDocument.GetElements())
+ {
+ auto pObject1 = dynamic_cast<vcl::filter::PDFObjectElement*>(rDocElement.get());
+ if (!pObject1)
+ continue;
+ auto pType1 = dynamic_cast<vcl::filter::PDFNameElement*>(pObject1->Lookup("Type"));
+ if (pType1 && pType1->GetValue() == "StructTreeRoot")
+ {
+ ++nRoots;
+ auto pParentTree
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObject1->Lookup("ParentTree"));
+ CPPUNIT_ASSERT(pParentTree);
+ auto pNumTree = pParentTree->LookupObject();
+ CPPUNIT_ASSERT(pNumTree);
+ auto pNums = dynamic_cast<vcl::filter::PDFArrayElement*>(pNumTree->Lookup("Nums"));
+ CPPUNIT_ASSERT(pNums);
+ auto nFound(0);
+ for (size_t i = 0; i < pNums->GetElements().size(); i += 2)
+ {
+ auto pI = dynamic_cast<vcl::filter::PDFNumberElement*>(pNums->GetElement(i));
+ if (pI->GetValue() == pStructParent->GetValue())
+ {
+ ++nFound;
+ CPPUNIT_ASSERT(i < pNums->GetElements().size() - 1);
+ pStructElemRef
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pNums->GetElement(i + 1));
+ CPPUNIT_ASSERT(pStructElemRef);
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nFound)>(1), nFound);
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRoots)>(1), nRoots);
+
+ // check /StructElem
+ CPPUNIT_ASSERT(pStructElemRef);
+ auto pStructElem(pStructElemRef->LookupObject());
+ CPPUNIT_ASSERT(pStructElem);
+
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("StructElem"), pType->GetValue());
+ auto pS = dynamic_cast<vcl::filter::PDFNameElement*>(pStructElem->Lookup("S"));
+ CPPUNIT_ASSERT_EQUAL(OString("Form"), pS->GetValue());
+ auto pAlt = dynamic_cast<vcl::filter::PDFHexStringElement*>(pStructElem->Lookup("Alt"));
+ CPPUNIT_ASSERT_EQUAL(OUString("textuelle alternative - a box to check"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt));
+ auto pA = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pStructElem->Lookup("A"));
+ CPPUNIT_ASSERT(pA);
+ auto pO = dynamic_cast<vcl::filter::PDFNameElement*>(pA->LookupElement("O"));
+ CPPUNIT_ASSERT(pO);
+ CPPUNIT_ASSERT_EQUAL(OString("PrintField"), pO->GetValue());
+ auto pRole = dynamic_cast<vcl::filter::PDFNameElement*>(pA->LookupElement("Role"));
+ CPPUNIT_ASSERT(pRole);
+ CPPUNIT_ASSERT_EQUAL(OString("cb"), pRole->GetValue());
+ auto pKids = dynamic_cast<vcl::filter::PDFArrayElement*>(pStructElem->Lookup("K"));
+ auto nMCID(0);
+ auto nRef(0);
+ for (size_t i = 0; i < pKids->GetElements().size(); ++i)
+ {
+ auto pNum = dynamic_cast<vcl::filter::PDFNumberElement*>(pKids->GetElement(i));
+ auto pRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pKids->GetElement(i));
+ if (pNum)
+ {
+ ++nMCID;
+ }
+ if (pRef)
+ {
+ ++nRef;
+ auto pObjR = pRef->LookupObject();
+ auto pOType = dynamic_cast<vcl::filter::PDFNameElement*>(pObjR->Lookup("Type"));
+ CPPUNIT_ASSERT_EQUAL(OString("OBJR"), pOType->GetValue());
+ auto pAnnotRef = dynamic_cast<vcl::filter::PDFReferenceElement*>(pObjR->Lookup("Obj"));
+ CPPUNIT_ASSERT_EQUAL(pAnnot, pAnnotRef->LookupObject());
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID)>(1), nMCID);
+ CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef)>(1), nRef);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf142129)
+{
+ loadFromURL(u"master.odm");
+
+ // update linked section
+ dispatchCommand(mxComponent, ".uno:UpdateAllLinks", {});
+
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // Enable Outlines export
+ uno::Sequence<beans::PropertyValue> aFilterData(
+ comphelper::InitPropertySequence({ { "ExportBookmarks", uno::Any(true) } }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ auto* pCatalog = aDocument.GetCatalog();
+ CPPUNIT_ASSERT(pCatalog);
+ auto* pCatalogDictionary = pCatalog->GetDictionary();
+ CPPUNIT_ASSERT(pCatalogDictionary);
+ auto* pOutlinesObject = pCatalogDictionary->LookupObject("Outlines");
+ CPPUNIT_ASSERT(pOutlinesObject);
+ auto* pOutlinesDictionary = pOutlinesObject->GetDictionary();
+#if 0
+ // Type isn't actually written currently
+ auto* pType
+ = dynamic_cast<vcl::filter::PDFNameElement*>(pOutlinesDictionary->LookupElement("Type"));
+ CPPUNIT_ASSERT(pType);
+ CPPUNIT_ASSERT_EQUAL(OString("Outlines"), pType->GetValue());
+#endif
+
+ auto* pFirst = dynamic_cast<vcl::filter::PDFReferenceElement*>(
+ pOutlinesDictionary->LookupElement("First"));
+ CPPUNIT_ASSERT(pFirst);
+ auto* pFirstD = pFirst->LookupObject()->GetDictionary();
+ CPPUNIT_ASSERT(pFirstD);
+ //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirstD->LookupElement("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(u"Preface"_ustr, ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(
+ pFirstD->LookupElement("Title"))));
+
+ auto* pFirst1
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirstD->LookupElement("First"));
+ CPPUNIT_ASSERT(pFirst1);
+ auto* pFirst1D = pFirst1->LookupObject()->GetDictionary();
+ CPPUNIT_ASSERT(pFirst1D);
+ // here is a hidden section with headings "Copyright" etc.; check that
+ // there are no outline entries for it
+ //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst1D->LookupElement("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ u"Who is this book for?"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst1D->LookupElement("Title"))));
+
+ auto* pFirst2
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirst1D->LookupElement("Next"));
+ auto* pFirst2D = pFirst2->LookupObject()->GetDictionary();
+ CPPUNIT_ASSERT(pFirst2D);
+ //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst2D->LookupElement("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ u"What\u2019s in this book?"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst2D->LookupElement("Title"))));
+
+ auto* pFirst3
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pFirst2D->LookupElement("Next"));
+ auto* pFirst3D = pFirst3->LookupObject()->GetDictionary();
+ CPPUNIT_ASSERT(pFirst3D);
+ //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst3D->LookupElement("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ u"Minimum requirements for using LibreOffice"_ustr,
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(pFirst3D->LookupElement("Title"))));
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement*>(nullptr),
+ pFirst3D->LookupElement("Next"));
+ CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement*>(nullptr),
+ pFirstD->LookupElement("Next"));
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageRotate180)
+{
+ // Create an empty document.
+ mxComponent = loadFromDesktop("private:factory/swriter");
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+
+ // Insert the PDF image.
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xGraphicObject(
+ xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+ OUString aURL = createFileURL(u"pdf-image-rotate-180.pdf");
+ xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
+ uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
+ xShape->setSize(awt::Size(1000, 1000));
+ uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
+
+ // Save as PDF.
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Parse the export result.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+ // Make sure that the page -> form -> form has a child image.
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
+ // 2: white background and the actual object.
+ CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject = pPageObject->getFormObject(1);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pFormObject->getType());
+ CPPUNIT_ASSERT_EQUAL(1, pFormObject->getFormObjectCount());
+
+ // Check if the inner form object (original page object in the pdf image) has the correct
+ // rotation.
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject = pFormObject->getFormObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pInnerFormObject->getType());
+ CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject->getFormObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pImage = pInnerFormObject->getFormObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image, pImage->getType());
+ basegfx::B2DHomMatrix aMat = pInnerFormObject->getMatrix();
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate = 0;
+ double fShearX = 0;
+ aMat.decompose(aScale, aTranslate, fRotate, fShearX);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: -1
+ // - Actual : 1
+ // i.e. the 180 degrees rotation didn't happen (via a combination of horizontal + vertical
+ // flip).
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, aScale.getX(), 0.01);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf144222)
+{
+// Assume Windows has the font for U+4E2D
+#ifdef _WIN32
+ aMediaDescriptor["FilterName"] <<= OUString("calc_pdf_Export");
+ saveAsPDF(u"tdf144222.ods");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ // The document has one page.
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
+ CPPUNIT_ASSERT(pTextPage);
+
+ int nPageObjectCount = pPdfPage->getObjectCount();
+ const OUString sChar = u"\u4E2D"_ustr;
+ basegfx::B2DRectangle aRect1, aRect2;
+ int nCount = 0;
+
+ for (int i = 0; i < nPageObjectCount; ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPdfPageObject = pPdfPage->getObject(i);
+ if (pPdfPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
+ {
+ ++nCount;
+ OUString sText = pPdfPageObject->getText(pTextPage);
+ if (sText == sChar)
+ aRect1 = pPdfPageObject->getBounds();
+ else
+ aRect2 = pPdfPageObject->getBounds();
+ }
+ }
+
+ CPPUNIT_ASSERT_EQUAL(2, nCount);
+ CPPUNIT_ASSERT(!aRect1.isEmpty());
+ CPPUNIT_ASSERT(!aRect2.isEmpty());
+ CPPUNIT_ASSERT(!aRect1.overlaps(aRect2));
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf145873)
+{
+ // Import the bugdoc and export as PDF.
+ aMediaDescriptor["FilterName"] <<= OUString("impress_pdf_Export");
+ saveAsPDF(u"tdf145873.pptx");
+
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ // The document has one page.
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ int nPageObjectCount = pPdfPage->getObjectCount();
+
+ // tdf#145873: Without the fix #1 in place, this test would have failed with
+ // - Expected: 66
+ // - Actual : 3
+ CPPUNIT_ASSERT_EQUAL(66, nPageObjectCount);
+
+ auto pObject = pPdfPage->getObject(4);
+ CPPUNIT_ASSERT_MESSAGE("no object", pObject != nullptr);
+
+ // tdf#145873: Without the fix #2 in place, this test would have failed with
+ // - Expected: 13.40
+ // - Actual : 3.57...
+ // - Delta : 0.1
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(13.40, pObject->getBounds().getWidth(), 0.1);
+ // - Expected: 13.79
+ // - Actual : 3.74...
+ // - Delta : 0.1
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(13.79, pObject->getBounds().getHeight(), 0.1);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageHyperlink)
+{
+ // Given a Draw file, containing a PDF image, which has a hyperlink in it:
+ aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
+
+ // When saving to PDF:
+ saveAsPDF(u"pdf-image-hyperlink.odg");
+
+ // Then make sure that link is preserved:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ // Without the accompanying fix in place, this test would have failed, the hyperlink of the PDF
+ // image was lost.
+ CPPUNIT_ASSERT(pPdfPage->hasLinks());
+
+ // Also test the precision of the form XObject.
+ // Given a full-page form XObject, page height is 27.94 cm (792 points):
+ // When writing the reciprocal of the object height to PDF:
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pFormObject;
+ for (int i = 0; i < pPdfPage->getObjectCount(); ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pObject = pPdfPage->getObject(i);
+ if (pObject->getType() == vcl::pdf::PDFPageObjectType::Form)
+ {
+ pFormObject = std::move(pObject);
+ break;
+ }
+ }
+ CPPUNIT_ASSERT(pFormObject);
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pInnerFormObject;
+ for (int i = 0; i < pFormObject->getFormObjectCount(); ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pObject = pFormObject->getFormObject(i);
+ if (pObject->getType() == vcl::pdf::PDFPageObjectType::Form)
+ {
+ pInnerFormObject = std::move(pObject);
+ break;
+ }
+ }
+ CPPUNIT_ASSERT(pInnerFormObject);
+ // Then make sure that enough digits are used, so the point size is unchanged:
+ basegfx::B2DHomMatrix aMatrix = pInnerFormObject->getMatrix();
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate{};
+ double fShearX{};
+ aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 0.0012626264
+ // - Actual : 0.00126
+ // i.e. the rounded reciprocal was 794 points, not the original 792.
+ CPPUNIT_ASSERT_EQUAL(0.0012626264, rtl::math::round(aScale.getY(), 10));
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testURIs)
+{
+ struct
+ {
+ OUString in;
+ OString out;
+ bool relativeFsys;
+ } URIs[] = { {
+ "http://example.com/",
+ "http://example.com/",
+ true,
+ },
+ {
+ "file://localfile.odt/",
+ "file://localfile.odt/",
+ true,
+ },
+ {
+ // tdf 143216
+ "http://username:password@example.com",
+ "http://username:password@example.com",
+ true,
+ },
+ {
+ "git://git.example.org/project/example",
+ "git://git.example.org/project/example",
+ true,
+ },
+ {
+ // The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
+ "filebypath.odt",
+ "filebypath.pdf",
+ true,
+ },
+ {
+ // The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
+ // but this time with ExportLinksRelativeFsys off the path is added
+ "filebypath.odt",
+ OUStringToOString(utl::GetTempNameBaseDirectory(), RTL_TEXTENCODING_UTF8)
+ + "filebypath.pdf",
+ false,
+ },
+ {
+ // This also gets made relative due to 'ExportLinksRelativeFsys'
+ utl::GetTempNameBaseDirectory() + "fileintempdir.odt",
+ "fileintempdir.pdf",
+ true,
+ } };
+
+ // Create an empty document.
+ // Note: The test harness gets very upset if we try and create multiple
+ // documents, or recreate it; so reuse one instance for all the links
+ mxComponent = loadFromDesktop("private:factory/swriter");
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ xText->insertString(xCursor, "Test pdf", /*bAbsorb=*/false);
+
+ // Set the name so it can do relative name replacement
+ uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
+ xModel->attachResource(maTempFile.GetURL(), xModel->getArgs());
+
+ for (unsigned int i = 0; i < (sizeof(URIs) / sizeof(URIs[0])); i++)
+ {
+ // Test the filename rewriting
+ uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence({
+ { "ExportLinksRelativeFsys", uno::Any(URIs[i].relativeFsys) },
+ { "ConvertOOoTargetToPDFTarget", uno::Any(true) },
+ }));
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+
+ // Add a link (based on testNestedHyperlink in rtfexport3)
+ xCursor->gotoStart(/*bExpand=*/false);
+ xCursor->gotoEnd(/*bExpand=*/true);
+ uno::Reference<beans::XPropertySet> xCursorProps(xCursor, uno::UNO_QUERY);
+ xCursorProps->setPropertyValue("HyperLinkURL", uno::Any(URIs[i].in));
+
+ // Save as PDF.
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Use the filter rather than the pdfium route, as per the tdf105093 test, it's
+ // easier to parse the annotations
+ vcl::filter::PDFDocument aDocument;
+
+ // Parse the export result.
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+ auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
+ CPPUNIT_ASSERT(pAnnots);
+
+ // There should be one annotation
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots->GetElements().size());
+ auto pAnnotReference
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[0]);
+ CPPUNIT_ASSERT(pAnnotReference);
+ vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
+ CPPUNIT_ASSERT(pAnnot);
+ // We're expecting something like /Type /Annot /A << /Type /Action /S /URI /URI (path)
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Annot"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Link"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
+ auto pAction = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pAnnot->Lookup("A"));
+ CPPUNIT_ASSERT(pAction);
+ auto pURIElem
+ = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pAction->LookupElement("URI"));
+ CPPUNIT_ASSERT(pURIElem);
+ // Check it matches
+ CPPUNIT_ASSERT_EQUAL(URIs[i].out, pURIElem->GetValue());
+ // tdf#148934 check a11y
+ CPPUNIT_ASSERT_EQUAL(
+ OUString("Test pdf"),
+ ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
+ *dynamic_cast<vcl::filter::PDFHexStringElement*>(pAnnot->Lookup("Contents"))));
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageAnnots)
+{
+ // Given a document with a PDF image that has 2 comments (popup, text) and a hyperlink:
+ aMediaDescriptor["FilterName"] <<= OUString("draw_pdf_Export");
+
+ // When saving to PDF:
+ saveAsPDF(u"pdf-image-annots.odg");
+
+ // Then make sure only the hyperlink is kept, since Draw itself has its own comments:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 1
+ // - Actual : 3
+ // i.e. not only the hyperlink but also the 2 comments were exported, leading to duplication.
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getAnnotationCount());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testPdfImageEncryption)
+{
+ // Given an empty document, with an inserted PDF image:
+ mxComponent = loadFromDesktop("private:factory/swriter");
+ uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
+ uno::Reference<text::XText> xText = xTextDocument->getText();
+ uno::Reference<text::XTextCursor> xCursor = xText->createTextCursor();
+ uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xGraphicObject(
+ xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY);
+ OUString aURL = createFileURL(u"rectangles.pdf");
+ xGraphicObject->setPropertyValue("GraphicURL", uno::Any(aURL));
+ uno::Reference<drawing::XShape> xShape(xGraphicObject, uno::UNO_QUERY);
+ xShape->setSize(awt::Size(1000, 1000));
+ uno::Reference<text::XTextContent> xTextContent(xGraphicObject, uno::UNO_QUERY);
+ xText->insertTextContent(xCursor->getStart(), xTextContent, /*bAbsorb=*/false);
+
+ // When saving as encrypted PDF:
+ uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ uno::Sequence<beans::PropertyValue> aFilterData = {
+ comphelper::makePropertyValue("EncryptFile", true),
+ comphelper::makePropertyValue("DocumentOpenPassword", OUString("secret")),
+ };
+ aMediaDescriptor["FilterData"] <<= aFilterData;
+ xStorable->storeToURL(maTempFile.GetURL(), aMediaDescriptor.getAsConstPropertyValueList());
+
+ // Then make sure that the image is not lost:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport("secret");
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form, pPageObject->getType());
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2
+ // - Actual : 0
+ // i.e. instead of the white background and the actual form child, the image was lost due to
+ // missing encryption.
+ CPPUNIT_ASSERT_EQUAL(2, pPageObject->getFormObjectCount());
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testBitmapScaledown)
+{
+ // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
+ // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
+ if (!IsDefaultDPI())
+ return;
+
+ // Given a document with an upscaled and rotated barcode bitmap in it:
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+
+ // When saving as PDF:
+ saveAsPDF(u"bitmap-scaledown.odt");
+
+ // Then verify that the bitmap is not downscaled:
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+ int nPageObjectCount = pPdfPage->getObjectCount();
+ for (int i = 0; i < nPageObjectCount; ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPdfPage->getObject(i);
+ if (pPageObject->getType() != vcl::pdf::PDFPageObjectType::Image)
+ continue;
+
+ std::unique_ptr<vcl::pdf::PDFiumBitmap> pBitmap = pPageObject->getImageBitmap();
+ CPPUNIT_ASSERT(pBitmap);
+ // In-file sizes: good is 2631x380, bad is 1565x14.
+ int nWidth = pBitmap->getWidth();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: 2616
+ // - Actual : 1565
+ // i.e. the bitmap in the pdf result was small enough to be blurry.
+ CPPUNIT_ASSERT_EQUAL(2616, nWidth);
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf139627)
+{
+#if HAVE_MORE_FONTS
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ saveAsPDF(u"justified-arabic-kashida.odt");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ // The document has one page.
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
+ CPPUNIT_ASSERT(pPdfPage);
+
+ // 7 objects, 3 text, others are path
+ int nPageObjectCount = pPdfPage->getObjectCount();
+ CPPUNIT_ASSERT_EQUAL(7, nPageObjectCount);
+
+ // 3 text objects
+ OUString sText[3];
+
+ /* With "Noto Sans Arabic" font, these are the X ranges on Linux:
+ 0: ( 61.75 - 415.94)
+ 1: (479.70 - 422.40)
+ 2: (209.40 - 453.2)
+ */
+ basegfx::B2DRectangle aRect[3];
+
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject;
+
+ int nTextObjectCount = 0;
+ for (int i = 0; i < nPageObjectCount; ++i)
+ {
+ pPageObject = pPdfPage->getObject(i);
+ CPPUNIT_ASSERT_MESSAGE("no object", pPageObject != nullptr);
+ if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
+ {
+ sText[nTextObjectCount] = pPageObject->getText(pTextPage);
+ aRect[nTextObjectCount] = pPageObject->getBounds();
+ ++nTextObjectCount;
+ }
+ }
+ CPPUNIT_ASSERT_EQUAL(3, nTextObjectCount);
+
+ // Text: جِـرم (which means "mass" in Persian)
+ // Rendered as (left to right): "reh + mim" - "kasreh" - "jeh + tatweel"
+ int rehmim = 0, kasreh = 1, jehtatweel = 2;
+
+ CPPUNIT_ASSERT_EQUAL(u"رم"_ustr, sText[rehmim].trim());
+ CPPUNIT_ASSERT_EQUAL(u""_ustr, sText[kasreh].trim());
+ CPPUNIT_ASSERT_EQUAL(u""_ustr, sText[jehtatweel].trim());
+
+ // "Kasreh" should be within "jeh" character
+ CPPUNIT_ASSERT_GREATER(aRect[jehtatweel].getMinX(), aRect[kasreh].getMinX());
+ CPPUNIT_ASSERT_LESS(aRect[jehtatweel].getMaxX(), aRect[kasreh].getMaxX());
+
+ // "Tatweel" should cover "jeh" and "reh"+"mim" to avoid gap
+ // Checking right gap
+ //CPPUNIT_ASSERT_GREATER(aRect[jehtatweel].getMinX(), aRect[tatweel].getMaxX());
+ // Checking left gap
+ // Kashida fails to reach to rehmim before the series of patches starting
+ // with 3901e029bd39575f700e69a73818565d62226a23. The visible symptom is
+ // a gap in the left of Kashida.
+ CPPUNIT_ASSERT_LESS(aRect[rehmim].getMaxX(), aRect[jehtatweel].getMinX());
+
+ // Overlappings of Kashida and surrounding characters is ~9% of the width
+ // of the "jeh" character, while using "Noto Arabic Sans" font in this
+ // specific example.
+ // We set the hard limit of 10% here.
+ CPPUNIT_ASSERT_LESS(0.1, fabs(aRect[rehmim].getMaxX() - aRect[jehtatweel].getMinX())
+ / aRect[jehtatweel].getWidth());
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportRefToKids)
+{
+ // We need to enable PDFium import (and make sure to disable after the test)
+ bool bResetEnvVar = false;
+ if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+ {
+ bResetEnvVar = true;
+ osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
+ }
+ comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+ if (bResetEnvVar)
+ osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
+ });
+
+ // Load the PDF and save as PDF
+ vcl::filter::PDFDocument aDocument;
+ load(u"ref-to-kids.pdf", aDocument);
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
+
+ vcl::filter::PDFObjectElement* pResources = aPages[0]->LookupObject("Resources");
+ CPPUNIT_ASSERT(pResources);
+
+ auto pXObjects
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pResources->Lookup("XObject"));
+ CPPUNIT_ASSERT(pXObjects);
+
+ // Without the fix LookupObject for all /Im's will fail.
+ for (auto const& rPair : pXObjects->GetItems())
+ {
+ if (rPair.first.startsWith("Im"))
+ CPPUNIT_ASSERT(pXObjects->LookupObject(rPair.first));
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportFilterSingletonArray)
+{
+ // We need to enable PDFium import (and make sure to disable after the test)
+ bool bResetEnvVar = false;
+ if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+ {
+ bResetEnvVar = true;
+ osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
+ }
+ comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+ if (bResetEnvVar)
+ osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
+ });
+
+ // Load the PDF and save as PDF
+ vcl::filter::PDFDocument aDocument;
+ // Loading fails with tagged PDF enabled
+ load(u"ref-to-kids.pdf", aDocument, false);
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
+
+ // Directly go to the inner XObject Im5 that contains the rectangle drawings.
+ auto pInnerIm = aDocument.LookupObject(5);
+ CPPUNIT_ASSERT(pInnerIm);
+
+ auto pFilter = dynamic_cast<vcl::filter::PDFNameElement*>(pInnerIm->Lookup("Filter"));
+ CPPUNIT_ASSERT(pFilter);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Filter must be FlateDecode", OString("FlateDecode"),
+ pFilter->GetValue());
+
+ vcl::filter::PDFStreamElement* pStream = pInnerIm->GetStream();
+ CPPUNIT_ASSERT(pStream);
+ SvMemoryStream& rObjectStream = pStream->GetMemory();
+ // Uncompress it.
+ SvMemoryStream aUncompressed;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ rObjectStream.Seek(0);
+ aZCodec.Decompress(rObjectStream, aUncompressed);
+ CPPUNIT_ASSERT(aZCodec.EndCompression());
+
+ // Without the fix, the stream is doubly compressed,
+ // hence one decompression will not yield the "re" expressions.
+ auto pStart = static_cast<const char*>(aUncompressed.GetData());
+ const char* pEnd = pStart + aUncompressed.GetSize();
+ OString aImage = "100 0 30 50 re B*\n70 67 50 30 re B*\n";
+ auto it = std::search(pStart, pEnd, aImage.getStr(), aImage.getStr() + aImage.getLength());
+ CPPUNIT_ASSERT(it != pEnd);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportMediaBoxOrigin)
+{
+ // We need to enable PDFium import (and make sure to disable after the test)
+ bool bResetEnvVar = false;
+ if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+ {
+ bResetEnvVar = true;
+ osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
+ }
+ comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+ if (bResetEnvVar)
+ osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
+ });
+
+ // Load the PDF and save as PDF
+ vcl::filter::PDFDocument aDocument;
+ load(u"ref-to-kids.pdf", aDocument);
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
+
+ // Directly go to the inner XObject Im12 that contains the rectangle drawings in page 2.
+ auto pInnerIm = aDocument.LookupObject(12);
+ CPPUNIT_ASSERT(pInnerIm);
+
+ constexpr sal_Int32 aOrigin[2] = { -800, -600 };
+ sal_Int32 aSize[2] = { 0, 0 };
+
+ auto pBBox = dynamic_cast<vcl::filter::PDFArrayElement*>(pInnerIm->Lookup("BBox"));
+ CPPUNIT_ASSERT(pBBox);
+ const auto& rElements2 = pBBox->GetElements();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements2.size());
+ for (sal_Int32 nIdx = 0; nIdx < 4; ++nIdx)
+ {
+ const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements2[nIdx]);
+ CPPUNIT_ASSERT(pNumElement);
+ if (nIdx < 2)
+ CPPUNIT_ASSERT_EQUAL(aOrigin[nIdx], static_cast<sal_Int32>(pNumElement->GetValue()));
+ else
+ aSize[nIdx - 2] = static_cast<sal_Int32>(pNumElement->GetValue()) - aOrigin[nIdx - 2];
+ }
+
+ auto pMatrix = dynamic_cast<vcl::filter::PDFArrayElement*>(pInnerIm->Lookup("Matrix"));
+ CPPUNIT_ASSERT(pMatrix);
+ const auto& rElements = pMatrix->GetElements();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(6), rElements.size());
+ sal_Int32 aMatTranslate[6]
+ = { // Rotation by $\theta$ $cos(\theta), sin(\theta), -sin(\theta), cos(\theta)$
+ 0, -1, 1, 0,
+ // Translate x,y
+ -aOrigin[1] - aSize[1] / 2 + aSize[0] / 2, aOrigin[0] + aSize[0] / 2 + aSize[1] / 2
+ };
+
+ for (sal_Int32 nIdx = 0; nIdx < 6; ++nIdx)
+ {
+ const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements[nIdx]);
+ CPPUNIT_ASSERT(pNumElement);
+ CPPUNIT_ASSERT_EQUAL(aMatTranslate[nIdx], static_cast<sal_Int32>(pNumElement->GetValue()));
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testRexportResourceItemReference)
+{
+ // We need to enable PDFium import (and make sure to disable after the test)
+ bool bResetEnvVar = false;
+ if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
+ {
+ bResetEnvVar = true;
+ osl_setEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData, OUString("1").pData);
+ }
+ comphelper::ScopeGuard aPDFiumEnvVarGuard([&]() {
+ if (bResetEnvVar)
+ osl_clearEnvironment(OUString("LO_IMPORT_USE_PDFIUM").pData);
+ });
+
+ // Load the PDF and save as PDF
+ vcl::filter::PDFDocument aDocument;
+ load(u"ref-to-kids.pdf", aDocument);
+
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(size_t(5), aPages.size());
+
+ // Directly go to the inner XObject Im12 that has reference to Font in page 2.
+ auto pInnerIm = aDocument.LookupObject(12);
+ CPPUNIT_ASSERT(pInnerIm);
+
+ auto pResources
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pInnerIm->Lookup("Resources"));
+ CPPUNIT_ASSERT(pResources);
+ auto pFontsReference
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pResources->LookupElement("Font"));
+ CPPUNIT_ASSERT(pFontsReference);
+
+ auto pFontsObject = pFontsReference->LookupObject();
+ CPPUNIT_ASSERT(pFontsObject);
+
+ auto pFontDict
+ = dynamic_cast<vcl::filter::PDFDictionaryElement*>(pFontsObject->Lookup("FF132"));
+ CPPUNIT_ASSERT(pFontDict);
+
+ auto pFontDescriptor = pFontDict->LookupObject("FontDescriptor");
+ CPPUNIT_ASSERT(pFontDescriptor);
+
+ auto pFontWidths = pFontDict->LookupObject("Widths");
+ CPPUNIT_ASSERT(pFontWidths);
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf152246)
+{
+ // Import the bugdoc and export as PDF.
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ saveAsPDF(u"content-control-rtl.docx");
+
+ // Parse the export result.
+ vcl::filter::PDFDocument aDocument;
+ SvFileStream aStream(maTempFile.GetURL(), StreamMode::READ);
+ CPPUNIT_ASSERT(aDocument.Read(aStream));
+
+ // The document has one page.
+ std::vector<vcl::filter::PDFObjectElement*> aPages = aDocument.GetPages();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages.size());
+
+ // Position array
+ constexpr double aPos[5][4] = { { 56.699, 707.701, 131.401, 721.499 },
+ { 198.499, 707.701, 273.201, 721.499 },
+ { 303.349, 680.101, 378.051, 693.899 },
+ { 480.599, 680.101, 555.301, 693.899 },
+ { 56.699, 652.501, 131.401, 666.299 } };
+
+ // Get page annotations.
+ auto pAnnots = dynamic_cast<vcl::filter::PDFArrayElement*>(aPages[0]->Lookup("Annots"));
+ CPPUNIT_ASSERT(pAnnots);
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), pAnnots->GetElements().size());
+ for (sal_Int32 i = 0; i < 5; ++i)
+ {
+ auto pAnnotReference
+ = dynamic_cast<vcl::filter::PDFReferenceElement*>(pAnnots->GetElements()[i]);
+ CPPUNIT_ASSERT(pAnnotReference);
+ vcl::filter::PDFObjectElement* pAnnot = pAnnotReference->LookupObject();
+ CPPUNIT_ASSERT(pAnnot);
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Annot"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Type"))->GetValue());
+ CPPUNIT_ASSERT_EQUAL(
+ OString("Widget"),
+ static_cast<vcl::filter::PDFNameElement*>(pAnnot->Lookup("Subtype"))->GetValue());
+
+ auto pRect = dynamic_cast<vcl::filter::PDFArrayElement*>(pAnnot->Lookup("Rect"));
+ CPPUNIT_ASSERT(pRect);
+ const auto& rElements = pRect->GetElements();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements.size());
+ for (sal_Int32 nIdx = 0; nIdx < 4; ++nIdx)
+ {
+ const auto* pNumElement = dynamic_cast<vcl::filter::PDFNumberElement*>(rElements[nIdx]);
+ CPPUNIT_ASSERT(pNumElement);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(aPos[i][nIdx], pNumElement->GetValue(), 1e-6);
+ }
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf155161)
+{
+// TODO: We seem to get a fallback font on Windows
+#ifndef _WIN32
+ vcl::filter::PDFDocument aDocument;
+ load(u"tdf155161.odt", aDocument);
+
+ // Check that all fonts in the document are Type 3 fonts
+ int nFonts = 0;
+ for (const auto& aElement : aDocument.GetElements())
+ {
+ auto pObject = dynamic_cast<vcl::filter::PDFObjectElement*>(aElement.get());
+ if (!pObject)
+ continue;
+ auto pType = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Type"));
+ if (pType && pType->GetValue() == "Font")
+ {
+ auto pSubtype = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Subtype"));
+ CPPUNIT_ASSERT(pSubtype);
+ CPPUNIT_ASSERT_EQUAL(OString("Type3"), pSubtype->GetValue());
+
+ auto pName = dynamic_cast<vcl::filter::PDFNameElement*>(pObject->Lookup("Name"));
+ CPPUNIT_ASSERT(pName);
+ CPPUNIT_ASSERT_EQUAL(OString("Cantarell-Regular"), pName->GetValue());
+
+ nFonts++;
+ }
+ }
+
+#ifdef MACOSX
+ // There must be two fonts
+ CPPUNIT_ASSERT_EQUAL(2, nFonts);
+#else
+ // But it seems that embedded variable fonts don’t register all supported
+ // styles on Linux, so the bold and regular text use the same regular font.
+ CPPUNIT_ASSERT(nFonts);
+#endif
+#endif
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf48707_1)
+{
+ // Import the bugdoc and export as PDF.
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ saveAsPDF(u"tdf48707-1.fodt");
+
+ // Parse the export result with pdfium.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+ auto pPage = pPdfDocument->openPage(0);
+ CPPUNIT_ASSERT(pPage);
+
+ int nPageObjectCount = pPage->getObjectCount();
+
+ CPPUNIT_ASSERT_EQUAL(6, nPageObjectCount);
+
+ auto pTextPage = pPage->getTextPage();
+
+ for (int i = 0; i < nPageObjectCount; ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(i);
+ // The text and path objects (underline and overline) should all be red.
+ if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
+ CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getFillColor());
+ else
+ CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getStrokeColor());
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf48707_2)
+{
+ // Import the bugdoc and export as PDF.
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ saveAsPDF(u"tdf48707-2.fodt");
+
+ // Parse the export result with pdfium.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
+
+ auto pPage = pPdfDocument->openPage(0);
+ CPPUNIT_ASSERT(pPage);
+
+ int nPageObjectCount = pPage->getObjectCount();
+
+ CPPUNIT_ASSERT_EQUAL(13, nPageObjectCount);
+
+ auto pTextPage = pPage->getTextPage();
+
+ for (int i = 0; i < nPageObjectCount; ++i)
+ {
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject = pPage->getObject(i);
+ if (pPageObject->getType() != vcl::pdf::PDFPageObjectType::Path)
+ continue;
+
+ // The table-like paths should be red, underline and overline should be black.
+ if (i >= 8)
+ CPPUNIT_ASSERT_EQUAL(COL_BLACK, pPageObject->getStrokeColor());
+ else
+ CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED, pPageObject->getStrokeColor());
+ }
+}
+
+CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf156528)
+{
+ aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
+ saveAsPDF(u"wide_page1.fodt");
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
+
+ // The document has two pages
+ CPPUNIT_ASSERT_EQUAL(2, pPdfDocument->getPageCount());
+
+ // 1st page (5100 mm width x 210 mm high, UserUnit = 2)
+ auto pPdfPage = pPdfDocument->openPage(0);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5100.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
+ pPdfPage->getWidth(), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
+ pPdfPage->getHeight(), 1);
+
+ // 1 object (rectangle 5060 mm width x 170 mm high, UserUnit = 2)
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ auto pRect = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT(pRect);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path, pRect->getType());
+ auto bounds = pRect->getBounds();
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5060.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
+ bounds.getWidth(), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0 / 2, o3tl::Length::mm, o3tl::Length::pt),
+ bounds.getHeight(), 1);
+
+ // 2nd page (210 mm width x 297 mm high, UserUnit = 1)
+ pPdfPage = pPdfDocument->openPage(1);
+ CPPUNIT_ASSERT(pPdfPage);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0, o3tl::Length::mm, o3tl::Length::pt),
+ pPdfPage->getWidth(), 1);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(297.0, o3tl::Length::mm, o3tl::Length::pt),
+ pPdfPage->getHeight(), 1);
+
+ // 1 object (rectangle 170 mm width x 257 mm high, UserUnit = 1)
+ CPPUNIT_ASSERT_EQUAL(1, pPdfPage->getObjectCount());
+ pRect = pPdfPage->getObject(0);
+ CPPUNIT_ASSERT(pRect);
+ CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path, pRect->getType());
+ bounds = pRect->getBounds();
+ // Without the fix, this would fail with
+ // - Expected: 481.889763779528
+ // - Actual : 241.925001144409
+ // - Delta : 1
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0, o3tl::Length::mm, o3tl::Length::pt),
+ bounds.getWidth(), 1);
+ //
+ // - Expected: 728.503937007874
+ // - Actual : 365.25
+ // - Delta : 1
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(257.0, o3tl::Length::mm, o3tl::Length::pt),
+ bounds.getHeight(), 1);
+}
+
+} // end anonymous namespace
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */