1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
13 #include <string_view>
15 #include <config_fonts.h>
16 #include <osl/process.h>
18 #include <com/sun/star/text/XDocumentIndexesSupplier.hpp>
19 #include <com/sun/star/util/XRefreshable.hpp>
20 #include <com/sun/star/beans/XPropertySet.hpp>
21 #include <com/sun/star/drawing/XShape.hpp>
22 #include <com/sun/star/text/XTextDocument.hpp>
23 #include <com/sun/star/document/XFilter.hpp>
24 #include <com/sun/star/document/XExporter.hpp>
25 #include <com/sun/star/io/XOutputStream.hpp>
27 #include <comphelper/scopeguard.hxx>
28 #include <comphelper/propertysequence.hxx>
29 #include <test/unoapi_test.hxx>
30 #include <unotools/mediadescriptor.hxx>
31 #include <unotools/tempfile.hxx>
32 #include <vcl/filter/pdfdocument.hxx>
33 #include <tools/zcodec.hxx>
34 #include <tools/XmlWalker.hxx>
35 #include <vcl/graphicfilter.hxx>
36 #include <basegfx/matrix/b2dhommatrix.hxx>
37 #include <unotools/streamwrap.hxx>
38 #include <rtl/math.hxx>
39 #include <o3tl/string_view.hxx>
40 #include <IDocumentDeviceAccess.hxx>
41 #include <printdata.hxx>
42 #include <unotxdoc.hxx>
46 #include <vcl/filter/PDFiumLibrary.hxx>
47 #include <vcl/pdfread.hxx>
48 #include <comphelper/propertyvalue.hxx>
51 using namespace ::com::sun::star
;
55 /// Tests the PDF export filter.
56 class PdfExportTest2
: public UnoApiTest
59 utl::MediaDescriptor aMediaDescriptor
;
63 : UnoApiTest(u
"/vcl/qa/cppunit/pdfexport/data/"_ustr
)
67 void saveAsPDF(std::u16string_view rFile
);
68 void load(std::u16string_view rFile
, vcl::filter::PDFDocument
& rDocument
);
71 void PdfExportTest2::saveAsPDF(std::u16string_view rFile
)
73 // Import the bugdoc and export as PDF.
75 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
76 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
79 void PdfExportTest2::load(std::u16string_view rFile
, vcl::filter::PDFDocument
& rDocument
)
83 // Parse the export result.
84 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
85 CPPUNIT_ASSERT(rDocument
.Read(aStream
));
88 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf159895
)
91 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
92 { "PDFUACompliance", uno::Any(true) },
93 { "ExportFormFields", uno::Any(true) },
95 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
97 vcl::filter::PDFDocument aDocument
;
98 load(u
"tdf159895.odt", aDocument
);
100 // The document has one page.
101 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
102 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
104 for (const auto& rDocElement
: aDocument
.GetElements())
106 auto pObj
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
110 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObj
->Lookup("Type"_ostr
));
111 if (pType
&& pType
->GetValue() == "XObject")
113 auto pFilter
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObj
->Lookup("Filter"_ostr
));
114 CPPUNIT_ASSERT(pFilter
);
116 vcl::filter::PDFStreamElement
* pStream
= pObj
->GetStream();
117 CPPUNIT_ASSERT(pStream
);
118 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
120 SvMemoryStream aUncompressed
;
122 aZCodec
.BeginCompression();
123 rObjectStream
.Seek(0);
124 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
125 CPPUNIT_ASSERT(aZCodec
.EndCompression());
127 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
128 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
133 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
137 std::string_view
const line(pStart
, pLine
- pStart
);
139 if (!line
.empty() && line
[0] != '%')
141 sText
+= line
+ "\n"_ostr
;
144 CPPUNIT_ASSERT_EQUAL("/Tx BMC\nEMC\n"_ostr
, sText
);
149 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf124272
)
151 // Import the bugdoc and export as PDF.
152 vcl::filter::PDFDocument aDocument
;
153 load(u
"tdf124272.odt", aDocument
);
155 // The document has one page.
156 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
157 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
159 // The page has a stream.
160 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
161 CPPUNIT_ASSERT(pContents
);
162 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
163 CPPUNIT_ASSERT(pStream
);
164 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
166 SvMemoryStream aUncompressed
;
168 aZCodec
.BeginCompression();
169 rObjectStream
.Seek(0);
170 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
171 CPPUNIT_ASSERT(aZCodec
.EndCompression());
173 OString
aBitmap("Q q 299.899 782.189 m\n"
174 "55.2 435.889 l 299.899 435.889 l 299.899 782.189 l\n"
177 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
178 const char* pEnd
= pStart
+ aUncompressed
.GetSize();
179 auto it
= std::search(pStart
, pEnd
, aBitmap
.getStr(), aBitmap
.getStr() + aBitmap
.getLength());
180 CPPUNIT_ASSERT(it
!= pEnd
);
183 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf121615
)
185 vcl::filter::PDFDocument aDocument
;
186 load(u
"tdf121615.odt", aDocument
);
188 // The document has one page.
189 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
190 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
192 // Get access to the only image on the only page.
193 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
194 CPPUNIT_ASSERT(pResources
);
196 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
197 CPPUNIT_ASSERT(pXObjects
);
198 CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects
->GetItems().size());
199 vcl::filter::PDFObjectElement
* pXObject
200 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
201 CPPUNIT_ASSERT(pXObject
);
202 vcl::filter::PDFStreamElement
* pStream
= pXObject
->GetStream();
203 CPPUNIT_ASSERT(pStream
);
204 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
206 // Load the embedded image.
207 rObjectStream
.Seek(0);
208 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
211 ErrCode bResult
= rFilter
.ImportGraphic(aGraphic
, u
"import", rObjectStream
,
212 GRFILTER_FORMAT_DONTKNOW
, &format
);
213 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE
, bResult
);
215 // The image should be grayscale 8bit JPEG.
216 sal_uInt16 jpegFormat
= rFilter
.GetImportFormatNumberForShortName(JPG_SHORTNAME
);
217 CPPUNIT_ASSERT(jpegFormat
!= GRFILTER_FORMAT_NOTFOUND
);
218 CPPUNIT_ASSERT_EQUAL(jpegFormat
, format
);
219 BitmapEx aBitmap
= aGraphic
.GetBitmapEx();
220 CPPUNIT_ASSERT_EQUAL(tools::Long(200), aBitmap
.GetSizePixel().Width());
221 CPPUNIT_ASSERT_EQUAL(tools::Long(300), aBitmap
.GetSizePixel().Height());
222 CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP
, aBitmap
.getPixelFormat());
223 // tdf#121615 was caused by broken handling of data width with 8bit color,
224 // so the test image has some black in the bottomright corner, check it's there
225 CPPUNIT_ASSERT_EQUAL(COL_WHITE
, aBitmap
.GetPixelColor(0, 0));
226 CPPUNIT_ASSERT_EQUAL(COL_WHITE
, aBitmap
.GetPixelColor(0, 299));
227 CPPUNIT_ASSERT_EQUAL(COL_WHITE
, aBitmap
.GetPixelColor(199, 0));
228 CPPUNIT_ASSERT_EQUAL(COL_BLACK
, aBitmap
.GetPixelColor(199, 299));
231 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf141171
)
233 vcl::filter::PDFDocument aDocument
;
234 load(u
"tdf141171.odt", aDocument
);
236 // The document has one page.
237 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
238 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
240 // Get access to the only image on the only page.
241 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
242 CPPUNIT_ASSERT(pResources
);
244 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
245 CPPUNIT_ASSERT(pXObjects
);
246 CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects
->GetItems().size());
247 vcl::filter::PDFObjectElement
* pXObject
248 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
249 CPPUNIT_ASSERT(pXObject
);
250 vcl::filter::PDFStreamElement
* pStream
= pXObject
->GetStream();
251 CPPUNIT_ASSERT(pStream
);
252 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
254 // Load the embedded image.
255 rObjectStream
.Seek(0);
256 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
259 ErrCode bResult
= rFilter
.ImportGraphic(aGraphic
, u
"import", rObjectStream
,
260 GRFILTER_FORMAT_DONTKNOW
, &format
);
261 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE
, bResult
);
263 // The image should be grayscale 8bit JPEG.
264 sal_uInt16 jpegFormat
= rFilter
.GetImportFormatNumberForShortName(JPG_SHORTNAME
);
265 CPPUNIT_ASSERT(jpegFormat
!= GRFILTER_FORMAT_NOTFOUND
);
266 CPPUNIT_ASSERT_EQUAL(jpegFormat
, format
);
267 BitmapEx aBitmap
= aGraphic
.GetBitmapEx();
268 Size aSize
= aBitmap
.GetSizePixel();
269 CPPUNIT_ASSERT_EQUAL(tools::Long(878), aSize
.Width());
270 CPPUNIT_ASSERT_EQUAL(tools::Long(127), aSize
.Height());
271 CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N8_BPP
, aBitmap
.getPixelFormat());
273 for (tools::Long nX
= 0; nX
< aSize
.Width(); ++nX
)
275 for (tools::Long nY
= 0; nY
< aSize
.Height(); ++nY
)
277 // Check all pixels in the image are white
278 // Without the fix in place, this test would have failed with
279 // - Expected: Color: R:255 G:255 B:255 A:0
280 // - Actual : Color: R:0 G:0 B:0 A:0
281 CPPUNIT_ASSERT_EQUAL(COL_WHITE
, aBitmap
.GetPixelColor(nX
, nY
));
286 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf161346
)
289 vcl::filter::PDFDocument aDocument
;
290 load(u
"fdo47811-1_Word2013.docx", aDocument
);
292 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
293 CPPUNIT_ASSERT_EQUAL(size_t(2), aPages
.size());
296 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf129085
)
298 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
299 // if the Quality is too high (like 90) then the image will be a DIB,
300 // not a JPEG! 80 works currently but set it lower to be sure...
301 { "Quality", uno::Any(sal_Int32(50)) },
303 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
305 vcl::filter::PDFDocument aDocument
;
306 load(u
"tdf129085.docx", aDocument
);
308 // The document has one page.
309 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
310 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
312 // Get access to the only image on the only page.
313 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
314 CPPUNIT_ASSERT(pResources
);
316 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
318 // Without the fix in place, this test would have failed here
319 CPPUNIT_ASSERT(pXObjects
);
320 CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects
->GetItems().size());
321 vcl::filter::PDFObjectElement
* pXObject
322 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
323 CPPUNIT_ASSERT(pXObject
);
324 vcl::filter::PDFStreamElement
* pStream
= pXObject
->GetStream();
325 CPPUNIT_ASSERT(pStream
);
326 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
328 // Load the embedded image.
329 rObjectStream
.Seek(0);
330 GraphicFilter
& rFilter
= GraphicFilter::GetGraphicFilter();
333 ErrCode bResult
= rFilter
.ImportGraphic(aGraphic
, u
"import", rObjectStream
,
334 GRFILTER_FORMAT_DONTKNOW
, &format
);
335 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE
, bResult
);
337 sal_uInt16 jpegFormat
= rFilter
.GetImportFormatNumberForShortName(JPG_SHORTNAME
);
338 CPPUNIT_ASSERT(jpegFormat
!= GRFILTER_FORMAT_NOTFOUND
);
339 CPPUNIT_ASSERT_EQUAL(jpegFormat
, format
);
340 BitmapEx aBitmap
= aGraphic
.GetBitmapEx();
341 CPPUNIT_ASSERT_EQUAL(tools::Long(884), aBitmap
.GetSizePixel().Width());
342 CPPUNIT_ASSERT_EQUAL(tools::Long(925), aBitmap
.GetSizePixel().Height());
343 CPPUNIT_ASSERT_EQUAL(vcl::PixelFormat::N24_BPP
, aBitmap
.getPixelFormat());
346 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTocLink
)
348 // Load the Writer document.
349 loadFromFile(u
"toc-link.fodt");
352 uno::Reference
<text::XDocumentIndexesSupplier
> xDocumentIndexesSupplier(mxComponent
,
354 CPPUNIT_ASSERT(xDocumentIndexesSupplier
.is());
356 uno::Reference
<util::XRefreshable
> xToc(
357 xDocumentIndexesSupplier
->getDocumentIndexes()->getByIndex(0), uno::UNO_QUERY
);
358 CPPUNIT_ASSERT(xToc
.is());
363 save(u
"writer_pdf_Export"_ustr
);
365 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
366 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
368 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
369 CPPUNIT_ASSERT(pPdfPage
);
371 // Ensure there is a link on the first page (in the ToC).
372 // Without the accompanying fix in place, this test would have failed, as the page contained no
374 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
377 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testReduceSmallImage
)
379 // Load the Writer document.
380 saveAsPDF(u
"reduce-small-image.fodt");
381 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
382 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
383 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
384 CPPUNIT_ASSERT(pPdfPage
);
385 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getObjectCount());
386 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(0);
387 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image
, pPageObject
->getType());
389 // Make sure we don't scale down a tiny bitmap.
390 std::unique_ptr
<vcl::pdf::PDFiumBitmap
> pBitmap
= pPageObject
->getImageBitmap();
391 CPPUNIT_ASSERT(pBitmap
);
392 int nWidth
= pBitmap
->getWidth();
393 int nHeight
= pBitmap
->getHeight();
394 // Without the accompanying fix in place, this test would have failed with:
397 // i.e. the image was scaled down to 300 DPI, even if it had tiny size.
398 CPPUNIT_ASSERT_EQUAL(16, nWidth
);
399 CPPUNIT_ASSERT_EQUAL(16, nHeight
);
402 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf114256
)
404 saveAsPDF(u
"tdf114256.ods");
405 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
406 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
407 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
408 CPPUNIT_ASSERT(pPdfPage
);
410 // Without the fix in place, this test would have failed with
413 CPPUNIT_ASSERT_EQUAL(13, pPdfPage
->getObjectCount());
416 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf150931
)
418 saveAsPDF(u
"tdf150931.ods");
419 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
420 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
421 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
422 CPPUNIT_ASSERT(pPdfPage
);
424 int nPageObjectCount
= pPdfPage
->getObjectCount();
425 // Without the fix in place, this test would have failed with
428 CPPUNIT_ASSERT_EQUAL(16, nPageObjectCount
);
430 int nYellowPathCount
= 0;
431 int nBlackPathCount
= 0;
432 int nGrayPathCount
= 0;
433 int nRedPathCount
= 0;
434 for (int i
= 0; i
< nPageObjectCount
; ++i
)
436 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPdfPageObject
= pPdfPage
->getObject(i
);
437 if (pPdfPageObject
->getType() != vcl::pdf::PDFPageObjectType::Path
)
440 int nSegments
= pPdfPageObject
->getPathSegmentCount();
441 CPPUNIT_ASSERT_EQUAL(5, nSegments
);
443 if (pPdfPageObject
->getFillColor() == COL_YELLOW
)
445 else if (pPdfPageObject
->getFillColor() == COL_BLACK
)
447 else if (pPdfPageObject
->getFillColor() == COL_GRAY
)
449 else if (pPdfPageObject
->getFillColor() == COL_LIGHTRED
)
453 CPPUNIT_ASSERT_EQUAL(3, nYellowPathCount
);
454 CPPUNIT_ASSERT_EQUAL(3, nRedPathCount
);
455 CPPUNIT_ASSERT_EQUAL(3, nGrayPathCount
);
456 CPPUNIT_ASSERT_EQUAL(3, nBlackPathCount
);
459 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf147027
)
461 // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
462 // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
466 // Load the Calc document.
467 saveAsPDF(u
"tdf147027.ods");
468 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
469 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
470 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
471 CPPUNIT_ASSERT(pPdfPage
);
473 // Without the fix in place, this test would have failed with
476 CPPUNIT_ASSERT_EQUAL(778, pPdfPage
->getObjectCount());
479 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf135346
)
481 // Load the Calc document.
482 saveAsPDF(u
"tdf135346.ods");
483 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
484 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
485 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
486 CPPUNIT_ASSERT(pPdfPage
);
488 // Without the fix in place, this test would have failed with
491 CPPUNIT_ASSERT_EQUAL(56, pPdfPage
->getObjectCount());
494 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf147164
)
496 saveAsPDF(u
"tdf147164.odp");
497 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
498 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
499 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/1);
500 CPPUNIT_ASSERT(pPdfPage
);
502 // Without the fix in place, this test would have failed with
505 CPPUNIT_ASSERT_EQUAL(22, pPdfPage
->getObjectCount());
508 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testReduceImage
)
510 // Load the Writer document.
511 loadFromFile(u
"reduce-image.fodt");
514 uno::Reference
<css::lang::XMultiServiceFactory
> xFactory
= getMultiServiceFactory();
515 uno::Reference
<document::XFilter
> xFilter(
516 xFactory
->createInstance(u
"com.sun.star.document.PDFFilter"_ustr
), uno::UNO_QUERY
);
517 uno::Reference
<document::XExporter
> xExporter(xFilter
, uno::UNO_QUERY
);
518 xExporter
->setSourceDocument(mxComponent
);
520 SvFileStream
aOutputStream(maTempFile
.GetURL(), StreamMode::WRITE
);
521 uno::Reference
<io::XOutputStream
> xOutputStream(new utl::OStreamWrapper(aOutputStream
));
523 uno::Sequence
<beans::PropertyValue
> aFilterData(
524 comphelper::InitPropertySequence({ { "ReduceImageResolution", uno::Any(false) } }));
526 // This is intentionally in an "unlucky" order, output stream comes before filter data.
527 uno::Sequence
<beans::PropertyValue
> aDescriptor(comphelper::InitPropertySequence({
528 { "FilterName", uno::Any(u
"writer_pdf_Export"_ustr
) },
529 { "OutputStream", uno::Any(xOutputStream
) },
530 { "FilterData", uno::Any(aFilterData
) },
532 xFilter
->filter(aDescriptor
);
533 aOutputStream
.Close();
535 // Parse the PDF: get the image.
536 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
537 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
538 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
539 CPPUNIT_ASSERT(pPdfPage
);
540 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getObjectCount());
541 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(0);
542 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image
, pPageObject
->getType());
544 // Make sure we don't scale down a bitmap.
545 std::unique_ptr
<vcl::pdf::PDFiumBitmap
> pBitmap
= pPageObject
->getImageBitmap();
546 CPPUNIT_ASSERT(pBitmap
);
547 int nWidth
= pBitmap
->getWidth();
548 int nHeight
= pBitmap
->getHeight();
549 // Without the accompanying fix in place, this test would have failed with:
552 // i.e. the image was scaled down even with ReduceImageResolution=false.
553 CPPUNIT_ASSERT_EQUAL(160, nWidth
);
554 CPPUNIT_ASSERT_EQUAL(160, nHeight
);
557 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testLinkWrongPage
)
559 // Import the bugdoc and export as PDF.
560 saveAsPDF(u
"link-wrong-page.odp");
561 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
563 // The document has 2 pages.
564 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
566 // First page should have 1 link (2nd slide, 1st was hidden).
567 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
568 CPPUNIT_ASSERT(pPdfPage
);
570 // Without the accompanying fix in place, this test would have failed, as the link of the first
571 // page went to the second page due to the hidden first slide.
572 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
574 // Second page should have no links (3rd slide).
575 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage2
= pPdfDocument
->openPage(/*nIndex=*/1);
576 CPPUNIT_ASSERT(pPdfPage2
);
577 CPPUNIT_ASSERT(!pPdfPage2
->hasLinks());
580 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testLinkWrongPagePartial
)
582 // Given a Draw document with 3 pages, a link on the 2nd page:
583 // When exporting that the 2nd and 3rd page to pdf:
584 uno::Sequence
<beans::PropertyValue
> aFilterData
= {
585 comphelper::makePropertyValue(u
"PageRange"_ustr
, u
"2-3"_ustr
),
587 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
588 saveAsPDF(u
"link-wrong-page-partial.odg");
590 // Then make sure the we have a link on the 1st page, but not on the 2nd one:
591 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
592 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
593 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
594 CPPUNIT_ASSERT(pPdfPage
);
595 // Without the accompanying fix in place, this test would have failed, as the link was on the
597 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
598 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage2
= pPdfDocument
->openPage(/*nIndex=*/1);
599 CPPUNIT_ASSERT(pPdfPage2
);
600 CPPUNIT_ASSERT(!pPdfPage2
->hasLinks());
603 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPageRange
)
605 // Given a document with 3 pages:
606 // When exporting that document to PDF, skipping the first page:
607 aMediaDescriptor
[u
"FilterOptions"_ustr
]
608 <<= u
"{\"PageRange\":{\"type\":\"string\",\"value\":\"2-\"}}"_ustr
;
609 saveAsPDF(u
"link-wrong-page-partial.odg");
611 // Then make sure the resulting PDF has 2 pages:
612 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
613 // Without the accompanying fix in place, this test would have failed with:
616 // i.e. FilterOptions was ignored.
617 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
620 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testLargePage
)
622 // Import the bugdoc and export as PDF.
623 saveAsPDF(u
"6m-wide.odg");
624 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
626 // The document has 1 page.
627 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
628 // Check the value (not the unit) of the page size.
629 basegfx::B2DSize aSize
= pPdfDocument
->getPageSize(0);
630 // Without the accompanying fix in place, this test would have failed with:
631 // - Expected: 8503.94
632 // - Actual : 17007.875
633 // i.e. the value for 600 cm was larger than the 14 400 limit set in the spec.
634 CPPUNIT_ASSERT_DOUBLES_EQUAL(8503.94, aSize
.getWidth(), 0.01);
637 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPdfImageResourceInlineXObjectRef
)
639 // Create an empty document.
640 loadFromURL(u
"private:factory/swriter"_ustr
);
641 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
642 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
643 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
645 // Insert the PDF image.
646 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
647 uno::Reference
<beans::XPropertySet
> xGraphicObject(
648 xFactory
->createInstance(u
"com.sun.star.text.TextGraphicObject"_ustr
), uno::UNO_QUERY
);
649 OUString aURL
= createFileURL(u
"pdf-image-resource-inline-xobject-ref.pdf");
650 xGraphicObject
->setPropertyValue(u
"GraphicURL"_ustr
, uno::Any(aURL
));
651 uno::Reference
<drawing::XShape
> xShape(xGraphicObject
, uno::UNO_QUERY
);
652 xShape
->setSize(awt::Size(1000, 1000));
653 uno::Reference
<text::XTextContent
> xTextContent(xGraphicObject
, uno::UNO_QUERY
);
654 xText
->insertTextContent(xCursor
->getStart(), xTextContent
, /*bAbsorb=*/false);
657 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
658 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
660 // Parse the export result.
661 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
662 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
664 // Make sure that the page -> form -> form has a child image.
665 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
666 CPPUNIT_ASSERT(pPdfPage
);
667 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getObjectCount());
668 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(0);
669 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form
, pPageObject
->getType());
670 // 2: white background and the actual object.
671 CPPUNIT_ASSERT_EQUAL(2, pPageObject
->getFormObjectCount());
672 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pFormObject
= pPageObject
->getFormObject(1);
673 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form
, pFormObject
->getType());
674 // Without the accompanying fix in place, this test would have failed with:
677 // i.e. the sub-form was missing its image.
678 CPPUNIT_ASSERT_EQUAL(1, pFormObject
->getFormObjectCount());
680 // Check if the inner form object (original page object in the pdf image) has the correct
682 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pInnerFormObject
= pFormObject
->getFormObject(0);
683 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form
, pInnerFormObject
->getType());
684 CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject
->getFormObjectCount());
685 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pImage
= pInnerFormObject
->getFormObject(0);
686 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image
, pImage
->getType());
687 basegfx::B2DHomMatrix aMat
= pInnerFormObject
->getMatrix();
688 basegfx::B2DTuple aScale
;
689 basegfx::B2DTuple aTranslate
;
692 aMat
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
693 int nRotateDeg
= basegfx::rad2deg(fRotate
);
694 // Without the accompanying fix in place, this test would have failed with:
697 // i.e. rotation was lost on pdf export.
698 CPPUNIT_ASSERT_EQUAL(-90, nRotateDeg
);
701 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testDefaultVersion
)
703 // Create an empty document.
704 loadFromURL(u
"private:factory/swriter"_ustr
);
707 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
708 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
710 // Parse the export result.
711 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
712 int nFileVersion
= pPdfDocument
->getFileVersion();
713 CPPUNIT_ASSERT_EQUAL(17, nFileVersion
);
716 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testVersion15
)
718 // Create an empty document.
719 loadFromURL(u
"private:factory/swriter"_ustr
);
722 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
723 { { "SelectPdfVersion", uno::Any(static_cast<sal_Int32
>(15)) } }));
724 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
725 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
726 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
728 // Parse the export result.
729 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
730 int nFileVersion
= pPdfDocument
->getFileVersion();
731 CPPUNIT_ASSERT_EQUAL(15, nFileVersion
);
734 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testVersion20
)
736 // Create an empty document.
737 mxComponent
= loadFromDesktop("private:factory/swriter");
740 uno::Reference
<frame::XStorable
> xStorable(mxComponent
, uno::UNO_QUERY
);
741 uno::Sequence
<beans::PropertyValue
> aFilterData
= comphelper::InitPropertySequence(
742 { { "SelectPdfVersion", uno::Any(static_cast<sal_Int32
>(20)) } });
743 aMediaDescriptor
["FilterName"] <<= OUString("writer_pdf_Export");
744 aMediaDescriptor
["FilterData"] <<= aFilterData
;
745 xStorable
->storeToURL(maTempFile
.GetURL(), aMediaDescriptor
.getAsConstPropertyValueList());
747 // Parse the export result.
748 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
749 int nFileVersion
= pPdfDocument
->getFileVersion();
750 CPPUNIT_ASSERT_EQUAL(20, nFileVersion
);
753 // Check round-trip of importing and exporting the PDF with PDFium filter,
754 // which imports the PDF document as multiple PDFs as graphic object.
755 // Each page in the document has one PDF graphic object which content is
756 // the corresponding page in the PDF. When such a document is exported,
757 // the PDF graphic gets embedded into the exported PDF document (as a
759 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testMultiPagePDF
)
761 // setenv only works on unix based systems
763 // We need to enable PDFium import (and make sure to disable after the test)
764 bool bResetEnvVar
= false;
765 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
768 setenv("LO_IMPORT_USE_PDFIUM", "1", false);
770 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
772 unsetenv("LO_IMPORT_USE_PDFIUM");
775 // Load the PDF and save as PDF
776 vcl::filter::PDFDocument aDocument
;
777 load(u
"SimpleMultiPagePDF.pdf", aDocument
);
779 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
780 CPPUNIT_ASSERT_EQUAL(size_t(3), aPages
.size());
782 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
783 CPPUNIT_ASSERT(pResources
);
786 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
787 CPPUNIT_ASSERT(pXObjects
);
789 CPPUNIT_ASSERT_EQUAL(size_t(3),
790 pXObjects
->GetItems().size()); // 3 PDFs as Form XObjects
792 std::vector
<OString
> rIDs
;
793 for (auto const& rPair
: pXObjects
->GetItems())
795 rIDs
.push_back(rPair
.first
);
798 // Let's check the embedded PDF pages - just make sure the size differs,
799 // which should indicate we don't have 3 times the same page.
801 { // embedded PDF page 1
802 vcl::filter::PDFObjectElement
* pXObject1
= pXObjects
->LookupObject(rIDs
[0]);
803 CPPUNIT_ASSERT(pXObject1
);
804 CPPUNIT_ASSERT_EQUAL("Im21"_ostr
, rIDs
[0]);
807 = dynamic_cast<vcl::filter::PDFNameElement
*>(pXObject1
->Lookup("Subtype"_ostr
));
808 CPPUNIT_ASSERT(pSubtype1
);
809 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pSubtype1
->GetValue());
811 auto pXObjectResources
812 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject1
->Lookup("Resources"_ostr
));
813 CPPUNIT_ASSERT(pXObjectResources
);
814 auto pXObjectForms
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
815 pXObjectResources
->LookupElement("XObject"_ostr
));
816 CPPUNIT_ASSERT(pXObjectForms
);
817 vcl::filter::PDFObjectElement
* pForm
818 = pXObjectForms
->LookupObject(pXObjectForms
->GetItems().begin()->first
);
819 CPPUNIT_ASSERT(pForm
);
821 vcl::filter::PDFStreamElement
* pStream
= pForm
->GetStream();
822 CPPUNIT_ASSERT(pStream
);
823 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
824 rObjectStream
.Seek(STREAM_SEEK_TO_BEGIN
);
826 SvMemoryStream aUncompressed
;
828 aZCodec
.BeginCompression();
829 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
830 CPPUNIT_ASSERT(aZCodec
.EndCompression());
832 // Just check that the size of the page stream is what is expected.
833 CPPUNIT_ASSERT_EQUAL(sal_uInt64(1236), aUncompressed
.Tell());
836 { // embedded PDF page 2
837 vcl::filter::PDFObjectElement
* pXObject2
= pXObjects
->LookupObject(rIDs
[1]);
838 CPPUNIT_ASSERT(pXObject2
);
839 CPPUNIT_ASSERT_EQUAL("Im27"_ostr
, rIDs
[1]);
842 = dynamic_cast<vcl::filter::PDFNameElement
*>(pXObject2
->Lookup("Subtype"_ostr
));
843 CPPUNIT_ASSERT(pSubtype2
);
844 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pSubtype2
->GetValue());
846 auto pXObjectResources
847 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject2
->Lookup("Resources"_ostr
));
848 CPPUNIT_ASSERT(pXObjectResources
);
849 auto pXObjectForms
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
850 pXObjectResources
->LookupElement("XObject"_ostr
));
851 CPPUNIT_ASSERT(pXObjectForms
);
852 vcl::filter::PDFObjectElement
* pForm
853 = pXObjectForms
->LookupObject(pXObjectForms
->GetItems().begin()->first
);
854 CPPUNIT_ASSERT(pForm
);
856 vcl::filter::PDFStreamElement
* pStream
= pForm
->GetStream();
857 CPPUNIT_ASSERT(pStream
);
858 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
859 rObjectStream
.Seek(STREAM_SEEK_TO_BEGIN
);
861 SvMemoryStream aUncompressed
;
863 aZCodec
.BeginCompression();
864 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
865 CPPUNIT_ASSERT(aZCodec
.EndCompression());
867 // Just check that the size of the page stream is what is expected.
868 CPPUNIT_ASSERT_EQUAL(sal_uInt64(3911), aUncompressed
.Tell());
871 { // embedded PDF page 3
872 vcl::filter::PDFObjectElement
* pXObject3
= pXObjects
->LookupObject(rIDs
[2]);
873 CPPUNIT_ASSERT(pXObject3
);
874 CPPUNIT_ASSERT_EQUAL("Im5"_ostr
, rIDs
[2]);
877 = dynamic_cast<vcl::filter::PDFNameElement
*>(pXObject3
->Lookup("Subtype"_ostr
));
878 CPPUNIT_ASSERT(pSubtype3
);
879 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pSubtype3
->GetValue());
881 auto pXObjectResources
882 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject3
->Lookup("Resources"_ostr
));
883 CPPUNIT_ASSERT(pXObjectResources
);
884 auto pXObjectForms
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
885 pXObjectResources
->LookupElement("XObject"_ostr
));
886 CPPUNIT_ASSERT(pXObjectForms
);
887 vcl::filter::PDFObjectElement
* pForm
888 = pXObjectForms
->LookupObject(pXObjectForms
->GetItems().begin()->first
);
889 CPPUNIT_ASSERT(pForm
);
891 vcl::filter::PDFStreamElement
* pStream
= pForm
->GetStream();
892 CPPUNIT_ASSERT(pStream
);
893 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
894 rObjectStream
.Seek(STREAM_SEEK_TO_BEGIN
);
896 SvMemoryStream aUncompressed
;
898 aZCodec
.BeginCompression();
899 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
900 CPPUNIT_ASSERT(aZCodec
.EndCompression());
902 // Just check that the size of the page stream is what is expected.
903 CPPUNIT_ASSERT_EQUAL(sal_uInt64(373), aUncompressed
.Tell());
908 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testFormFontName
)
910 // Import the bugdoc and export as PDF.
911 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
912 { "ExportFormFields", uno::Any(true) },
914 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
915 saveAsPDF(u
"form-font-name.odt");
917 // Parse the export result with pdfium.
918 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
920 // The document has one page.
921 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
922 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
923 CPPUNIT_ASSERT(pPdfPage
);
925 // The page has one annotation.
926 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getAnnotationCount());
927 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnot
= pPdfPage
->getAnnotation(0);
929 // Examine the default appearance.
930 CPPUNIT_ASSERT(pAnnot
->hasKey("DA"_ostr
));
931 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFObjectType::String
, pAnnot
->getValueType("DA"_ostr
));
932 OUString aDA
= pAnnot
->getString("DA"_ostr
);
934 // Without the accompanying fix in place, this test would have failed with:
935 // - Expected: 0 0 0 rg /TiRo 12 Tf
936 // - Actual : 0 0 0 rg /F2 12 Tf
937 // i.e. Liberation Serif was exposed as a form font as-is, without picking the closest built-in
939 CPPUNIT_ASSERT_EQUAL(u
"0 0 0 rg /TiRo 12 Tf"_ustr
, aDA
);
942 // Check we don't have duplicated objects when we reexport the PDF multiple
943 // times or the size will exponentially increase over time.
944 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testReexportPDF
)
946 // setenv only works on unix based systems
948 // We need to enable PDFium import (and make sure to disable after the test)
949 bool bResetEnvVar
= false;
950 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
953 setenv("LO_IMPORT_USE_PDFIUM", "1", false);
955 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
957 unsetenv("LO_IMPORT_USE_PDFIUM");
960 // Load the PDF and save as PDF
961 vcl::filter::PDFDocument aDocument
;
962 load(u
"PDFWithImages.pdf", aDocument
);
964 // Assert that the XObject in the page resources dictionary is a reference XObject.
965 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
967 // The document has 2 pages.
968 CPPUNIT_ASSERT_EQUAL(size_t(2), aPages
.size());
972 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
973 CPPUNIT_ASSERT(pResources
);
976 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
977 CPPUNIT_ASSERT(pXObjects
);
979 std::vector
<OString
> rIDs
;
980 for (auto const& rPair
: pXObjects
->GetItems())
981 rIDs
.push_back(rPair
.first
);
983 CPPUNIT_ASSERT_EQUAL(size_t(2), rIDs
.size());
985 std::vector
<int> aBitmapRefs1
;
986 std::vector
<int> aBitmapRefs2
;
990 OString aID
= rIDs
[0];
991 CPPUNIT_ASSERT_EQUAL("Im14"_ostr
, aID
);
992 vcl::filter::PDFObjectElement
* pXObject
= pXObjects
->LookupObject(aID
);
993 CPPUNIT_ASSERT(pXObject
);
996 = dynamic_cast<vcl::filter::PDFNameElement
*>(pXObject
->Lookup("Subtype"_ostr
));
997 CPPUNIT_ASSERT(pSubtype
);
998 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pSubtype
->GetValue());
1000 auto pInnerResources
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1001 pXObject
->Lookup("Resources"_ostr
));
1002 CPPUNIT_ASSERT(pInnerResources
);
1003 auto pInnerXObjects
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1004 pInnerResources
->LookupElement("XObject"_ostr
));
1005 CPPUNIT_ASSERT(pInnerXObjects
);
1006 CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects
->GetItems().size());
1007 OString aInnerObjectID
= pInnerXObjects
->GetItems().begin()->first
;
1008 CPPUNIT_ASSERT_EQUAL("Im15"_ostr
, aInnerObjectID
);
1010 vcl::filter::PDFObjectElement
* pInnerXObject
1011 = pInnerXObjects
->LookupObject(aInnerObjectID
);
1012 CPPUNIT_ASSERT(pInnerXObject
);
1015 = dynamic_cast<vcl::filter::PDFNameElement
*>(pInnerXObject
->Lookup("Subtype"_ostr
));
1016 CPPUNIT_ASSERT(pInnerSubtype
);
1017 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pInnerSubtype
->GetValue());
1019 auto pInnerInnerResources
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1020 pInnerXObject
->Lookup("Resources"_ostr
));
1021 CPPUNIT_ASSERT(pInnerInnerResources
);
1022 auto pInnerInnerXObjects
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1023 pInnerInnerResources
->LookupElement("XObject"_ostr
));
1024 CPPUNIT_ASSERT(pInnerInnerXObjects
);
1025 CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects
->GetItems().size());
1027 std::vector
<OString
> aBitmapIDs1
;
1028 for (auto const& rPair
: pInnerInnerXObjects
->GetItems())
1029 aBitmapIDs1
.push_back(rPair
.first
);
1032 CPPUNIT_ASSERT_EQUAL("Im11"_ostr
, aBitmapIDs1
[0]);
1033 auto pRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1034 pInnerInnerXObjects
->LookupElement(aBitmapIDs1
[0]));
1035 CPPUNIT_ASSERT(pRef
);
1036 aBitmapRefs1
.push_back(pRef
->GetObjectValue());
1037 CPPUNIT_ASSERT_EQUAL(0, pRef
->GetGenerationValue());
1039 vcl::filter::PDFObjectElement
* pBitmap
1040 = pInnerInnerXObjects
->LookupObject(aBitmapIDs1
[0]);
1041 CPPUNIT_ASSERT(pBitmap
);
1043 = dynamic_cast<vcl::filter::PDFNameElement
*>(pBitmap
->Lookup("Subtype"_ostr
));
1044 CPPUNIT_ASSERT(pBitmapSubtype
);
1045 CPPUNIT_ASSERT_EQUAL("Image"_ostr
, pBitmapSubtype
->GetValue());
1048 CPPUNIT_ASSERT_EQUAL("Im5"_ostr
, aBitmapIDs1
[1]);
1049 auto pRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1050 pInnerInnerXObjects
->LookupElement(aBitmapIDs1
[1]));
1051 CPPUNIT_ASSERT(pRef
);
1052 aBitmapRefs1
.push_back(pRef
->GetObjectValue());
1053 CPPUNIT_ASSERT_EQUAL(0, pRef
->GetGenerationValue());
1055 vcl::filter::PDFObjectElement
* pBitmap
1056 = pInnerInnerXObjects
->LookupObject(aBitmapIDs1
[1]);
1057 CPPUNIT_ASSERT(pBitmap
);
1059 = dynamic_cast<vcl::filter::PDFNameElement
*>(pBitmap
->Lookup("Subtype"_ostr
));
1060 CPPUNIT_ASSERT(pBitmapSubtype
);
1061 CPPUNIT_ASSERT_EQUAL("Image"_ostr
, pBitmapSubtype
->GetValue());
1067 OString aID
= rIDs
[1];
1068 CPPUNIT_ASSERT_EQUAL("Im5"_ostr
, aID
);
1069 vcl::filter::PDFObjectElement
* pXObject
= pXObjects
->LookupObject(aID
);
1070 CPPUNIT_ASSERT(pXObject
);
1073 = dynamic_cast<vcl::filter::PDFNameElement
*>(pXObject
->Lookup("Subtype"_ostr
));
1074 CPPUNIT_ASSERT(pSubtype
);
1075 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pSubtype
->GetValue());
1077 auto pInnerResources
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1078 pXObject
->Lookup("Resources"_ostr
));
1079 CPPUNIT_ASSERT(pInnerResources
);
1080 auto pInnerXObjects
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1081 pInnerResources
->LookupElement("XObject"_ostr
));
1082 CPPUNIT_ASSERT(pInnerXObjects
);
1083 CPPUNIT_ASSERT_EQUAL(size_t(1), pInnerXObjects
->GetItems().size());
1084 OString aInnerObjectID
= pInnerXObjects
->GetItems().begin()->first
;
1085 CPPUNIT_ASSERT_EQUAL("Im6"_ostr
, aInnerObjectID
);
1087 vcl::filter::PDFObjectElement
* pInnerXObject
1088 = pInnerXObjects
->LookupObject(aInnerObjectID
);
1089 CPPUNIT_ASSERT(pInnerXObject
);
1092 = dynamic_cast<vcl::filter::PDFNameElement
*>(pInnerXObject
->Lookup("Subtype"_ostr
));
1093 CPPUNIT_ASSERT(pInnerSubtype
);
1094 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pInnerSubtype
->GetValue());
1096 auto pInnerInnerResources
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1097 pInnerXObject
->Lookup("Resources"_ostr
));
1098 CPPUNIT_ASSERT(pInnerInnerResources
);
1099 auto pInnerInnerXObjects
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
1100 pInnerInnerResources
->LookupElement("XObject"_ostr
));
1101 CPPUNIT_ASSERT(pInnerInnerXObjects
);
1102 CPPUNIT_ASSERT_EQUAL(size_t(2), pInnerInnerXObjects
->GetItems().size());
1104 std::vector
<OString
> aBitmapIDs2
;
1105 for (auto const& rPair
: pInnerInnerXObjects
->GetItems())
1106 aBitmapIDs2
.push_back(rPair
.first
);
1109 CPPUNIT_ASSERT_EQUAL("Im11"_ostr
, aBitmapIDs2
[0]);
1110 auto pRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1111 pInnerInnerXObjects
->LookupElement(aBitmapIDs2
[0]));
1112 CPPUNIT_ASSERT(pRef
);
1113 aBitmapRefs2
.push_back(pRef
->GetObjectValue());
1114 CPPUNIT_ASSERT_EQUAL(0, pRef
->GetGenerationValue());
1116 vcl::filter::PDFObjectElement
* pBitmap
1117 = pInnerInnerXObjects
->LookupObject(aBitmapIDs2
[0]);
1118 CPPUNIT_ASSERT(pBitmap
);
1120 = dynamic_cast<vcl::filter::PDFNameElement
*>(pBitmap
->Lookup("Subtype"_ostr
));
1121 CPPUNIT_ASSERT(pBitmapSubtype
);
1122 CPPUNIT_ASSERT_EQUAL("Image"_ostr
, pBitmapSubtype
->GetValue());
1125 CPPUNIT_ASSERT_EQUAL("Im5"_ostr
, aBitmapIDs2
[1]);
1126 auto pRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1127 pInnerInnerXObjects
->LookupElement(aBitmapIDs2
[1]));
1128 CPPUNIT_ASSERT(pRef
);
1129 aBitmapRefs2
.push_back(pRef
->GetObjectValue());
1130 CPPUNIT_ASSERT_EQUAL(0, pRef
->GetGenerationValue());
1132 vcl::filter::PDFObjectElement
* pBitmap
1133 = pInnerInnerXObjects
->LookupObject(aBitmapIDs2
[1]);
1134 CPPUNIT_ASSERT(pBitmap
);
1136 = dynamic_cast<vcl::filter::PDFNameElement
*>(pBitmap
->Lookup("Subtype"_ostr
));
1137 CPPUNIT_ASSERT(pBitmapSubtype
);
1138 CPPUNIT_ASSERT_EQUAL("Image"_ostr
, pBitmapSubtype
->GetValue());
1141 // Ref should point to the same bitmap
1142 CPPUNIT_ASSERT_EQUAL(aBitmapRefs1
[0], aBitmapRefs2
[0]);
1143 CPPUNIT_ASSERT_EQUAL(aBitmapRefs1
[1], aBitmapRefs2
[1]);
1149 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf160117
)
1151 vcl::filter::PDFDocument aDocument
;
1152 load(u
"tdf160117.ods", aDocument
);
1154 // The document has one page.
1155 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1156 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
1159 bool bFound1
= false;
1160 bool bFound2
= false;
1161 bool bFound3
= false;
1162 for (const auto& rDocElement
: aDocument
.GetElements())
1164 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
1167 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1168 if (pType
&& pType
->GetValue() == "FontDescriptor")
1171 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("FontName"_ostr
));
1172 CPPUNIT_ASSERT(pFontName
);
1173 if ("CAAAAA+LiberationSans-Bold"_ostr
== pFontName
->GetValue())
1175 else if ("DAAAAA+LiberationSans-Italic"_ostr
== pFontName
->GetValue())
1177 else if ("BAAAAA+LiberationSans"_ostr
== pFontName
->GetValue())
1182 // Without the fix in place, this test would have failed with
1185 CPPUNIT_ASSERT_EQUAL(3, nCount
);
1186 CPPUNIT_ASSERT(bFound1
);
1187 CPPUNIT_ASSERT(bFound2
);
1188 CPPUNIT_ASSERT(bFound3
);
1191 // Check we correctly copy more complex resources (Fonts describing
1192 // glyphs in recursive arrays) to the target PDF
1193 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testReexportDocumentWithComplexResources
)
1195 // setenv only works on unix based systems
1197 // We need to enable PDFium import (and make sure to disable after the test)
1198 bool bResetEnvVar
= false;
1199 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
1201 bResetEnvVar
= true;
1202 setenv("LO_IMPORT_USE_PDFIUM", "1", false);
1204 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
1206 unsetenv("LO_IMPORT_USE_PDFIUM");
1209 // Load the PDF and save as PDF
1210 vcl::filter::PDFDocument aDocument
;
1211 load(u
"ComplexContentDictionary.pdf", aDocument
);
1213 // Assert that the XObject in the page resources dictionary is a reference XObject.
1214 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1215 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
1217 // Go directly to the Font object (24 0) (number could change if we change how PDF export works)
1218 auto pFont
= aDocument
.LookupObject(24);
1219 CPPUNIT_ASSERT(pFont
);
1221 // Check it is the Font object (Type = Font)
1222 auto pName
= dynamic_cast<vcl::filter::PDFNameElement
*>(
1223 pFont
->GetDictionary()->LookupElement("Type"_ostr
));
1224 CPPUNIT_ASSERT(pName
);
1225 CPPUNIT_ASSERT_EQUAL("Font"_ostr
, pName
->GetValue());
1227 // Check BaseFont is what we expect
1228 auto pBaseFont
= dynamic_cast<vcl::filter::PDFNameElement
*>(
1229 pFont
->GetDictionary()->LookupElement("BaseFont"_ostr
));
1230 CPPUNIT_ASSERT(pBaseFont
);
1231 CPPUNIT_ASSERT_EQUAL("HOTOMR+Calibri,Italic"_ostr
, pBaseFont
->GetValue());
1233 // Check and get the W array
1234 auto pWArray
= dynamic_cast<vcl::filter::PDFArrayElement
*>(
1235 pFont
->GetDictionary()->LookupElement("W"_ostr
));
1236 CPPUNIT_ASSERT(pWArray
);
1237 CPPUNIT_ASSERT_EQUAL(size_t(26), pWArray
->GetElements().size());
1239 // Check the content of W array
1240 // ObjectCopier didn't copy this array correctly and the document
1241 // had glyphs at the wrong places
1244 auto pNumberAtIndex0
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pWArray
->GetElement(0));
1245 CPPUNIT_ASSERT(pNumberAtIndex0
);
1246 CPPUNIT_ASSERT_EQUAL(3.0, pNumberAtIndex0
->GetValue());
1248 auto pArrayAtIndex1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pWArray
->GetElement(1));
1249 CPPUNIT_ASSERT(pArrayAtIndex1
);
1250 CPPUNIT_ASSERT_EQUAL(size_t(1), pArrayAtIndex1
->GetElements().size());
1254 = dynamic_cast<vcl::filter::PDFNumberElement
*>(pArrayAtIndex1
->GetElement(0));
1255 CPPUNIT_ASSERT(pNumber
);
1256 CPPUNIT_ASSERT_EQUAL(226.0, pNumber
->GetValue());
1260 auto pNumberAtIndex24
1261 = dynamic_cast<vcl::filter::PDFNumberElement
*>(pWArray
->GetElement(24));
1262 CPPUNIT_ASSERT(pNumberAtIndex24
);
1263 CPPUNIT_ASSERT_EQUAL(894.0, pNumberAtIndex24
->GetValue());
1265 auto pArrayAtIndex25
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pWArray
->GetElement(25));
1266 CPPUNIT_ASSERT(pArrayAtIndex25
);
1267 CPPUNIT_ASSERT_EQUAL(size_t(2), pArrayAtIndex25
->GetElements().size());
1271 = dynamic_cast<vcl::filter::PDFNumberElement
*>(pArrayAtIndex25
->GetElement(0));
1272 CPPUNIT_ASSERT(pNumber1
);
1273 CPPUNIT_ASSERT_EQUAL(303.0, pNumber1
->GetValue());
1276 = dynamic_cast<vcl::filter::PDFNumberElement
*>(pArrayAtIndex25
->GetElement(1));
1277 CPPUNIT_ASSERT(pNumber2
);
1278 CPPUNIT_ASSERT_EQUAL(303.0, pNumber2
->GetValue());
1284 // Tests that at export the PDF has the PDF/UA metadata properly set
1285 // when we enable PDF/UA support.
1286 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPdfUaMetadata
)
1289 uno::Sequence
<beans::PropertyValue
> aFilterData(
1290 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1291 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1293 vcl::filter::PDFDocument aDocument
;
1294 load(u
"BrownFoxLazyDog.odt", aDocument
);
1296 auto* pCatalog
= aDocument
.GetCatalog();
1297 CPPUNIT_ASSERT(pCatalog
);
1298 auto* pCatalogDictionary
= pCatalog
->GetDictionary();
1299 CPPUNIT_ASSERT(pCatalogDictionary
);
1300 auto* pMetadataObject
= pCatalogDictionary
->LookupObject("Metadata"_ostr
);
1301 CPPUNIT_ASSERT(pMetadataObject
);
1302 auto* pMetadataDictionary
= pMetadataObject
->GetDictionary();
1303 auto* pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(
1304 pMetadataDictionary
->LookupElement("Type"_ostr
));
1305 CPPUNIT_ASSERT(pType
);
1306 CPPUNIT_ASSERT_EQUAL("Metadata"_ostr
, pType
->GetValue());
1308 auto* pStreamObject
= pMetadataObject
->GetStream();
1309 CPPUNIT_ASSERT(pStreamObject
);
1310 auto& rStream
= pStreamObject
->GetMemory();
1313 // Search for the PDF/UA marker in the metadata
1315 tools::XmlWalker aWalker
;
1316 CPPUNIT_ASSERT(aWalker
.open(&rStream
));
1317 CPPUNIT_ASSERT_EQUAL(std::string_view("xmpmeta"), aWalker
.name());
1319 bool bPdfUaMarkerFound
= false;
1323 while (aWalker
.isValid())
1325 if (aWalker
.name() == "RDF"
1326 && aWalker
.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
1329 while (aWalker
.isValid())
1331 if (aWalker
.name() == "Description"
1332 && aWalker
.namespaceHref() == "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
1335 while (aWalker
.isValid())
1337 if (aWalker
.name() == "part"
1338 && aWalker
.namespaceHref() == "http://www.aiim.org/pdfua/ns/id/")
1340 aPdfUaPart
= aWalker
.content();
1341 bPdfUaMarkerFound
= true;
1355 CPPUNIT_ASSERT(bPdfUaMarkerFound
);
1356 CPPUNIT_ASSERT_EQUAL("1"_ostr
, aPdfUaPart
);
1359 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf139736
)
1362 uno::Sequence
<beans::PropertyValue
> aFilterData(
1363 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
1364 { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
1365 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1367 vcl::filter::PDFDocument aDocument
;
1368 load(u
"tdf139736-1.odt", aDocument
);
1370 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1371 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
1373 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1374 CPPUNIT_ASSERT(pContents
);
1375 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
1376 CPPUNIT_ASSERT(pStream
);
1377 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
1379 SvMemoryStream aUncompressed
;
1381 aZCodec
.BeginCompression();
1382 rObjectStream
.Seek(0);
1383 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1384 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1386 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
1387 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
1405 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
1410 std::string_view
const line(pStart
, pLine
- pStart
);
1412 if (!line
.empty() && line
[0] != '%')
1414 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
1415 if (line
== "/Artifact BMC")
1417 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1421 else if (o3tl::starts_with(line
, "/Artifact <<"))
1423 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1424 // check header/footer properties
1425 CPPUNIT_ASSERT_EQUAL(std::string_view("/Type/Pagination"), line
.substr(12));
1426 state
= ArtifactProps1
;
1429 else if (state
== ArtifactProps1
)
1431 CPPUNIT_ASSERT_EQUAL(std::string_view("/Subtype/Header"), line
);
1432 state
= ArtifactProps2
;
1434 else if (state
== ArtifactProps2
&& line
== ">> BDC")
1438 else if (line
== "/Standard<</MCID 0>>BDC")
1440 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1444 else if (line
== "EMC")
1446 CPPUNIT_ASSERT_MESSAGE("unexpected end", state
!= Default
);
1449 else if (nLine
> 1) // first line is expected "0.1 w"
1451 CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state
!= Default
);
1455 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
1456 CPPUNIT_ASSERT_EQUAL(1, nTagged
); // text in body
1457 // 1 image and 1 frame and 1 header text; arbitrary number of aux stuff like borders
1458 CPPUNIT_ASSERT_GREATEREQUAL(3, nArtifacts
);
1461 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf152231
)
1464 uno::Sequence
<beans::PropertyValue
> aFilterData(
1465 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) },
1466 { "ExportNotesInMargin", uno::Any(true) },
1467 { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
1468 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1469 vcl::filter::PDFDocument aDocument
;
1470 load(u
"tdf152231.fodt", aDocument
);
1472 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1473 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
1475 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1476 CPPUNIT_ASSERT(pContents
);
1477 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
1478 CPPUNIT_ASSERT(pStream
);
1479 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
1481 SvMemoryStream aUncompressed
;
1483 aZCodec
.BeginCompression();
1484 rObjectStream
.Seek(0);
1485 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1486 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1488 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
1489 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
1505 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
1510 std::string_view
const line(pStart
, pLine
- pStart
);
1512 if (!line
.empty() && line
[0] != '%')
1514 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
1515 if (line
== "/Artifact BMC")
1517 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1521 else if (o3tl::starts_with(line
, "/Standard<</MCID "))
1523 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1527 else if (line
== "EMC")
1529 CPPUNIT_ASSERT_MESSAGE("unexpected end", state
!= Default
);
1532 else if (nLine
> 1) // first line is expected "0.1 w"
1534 CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state
!= Default
);
1538 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
1539 CPPUNIT_ASSERT_GREATEREQUAL(12, nTagged
); // text in body
1541 CPPUNIT_ASSERT_GREATEREQUAL(1, nArtifacts
);
1544 for (const auto& rDocElement
: aDocument
.GetElements())
1546 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
1549 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1550 if (pType
&& pType
->GetValue() == "StructElem")
1552 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
1553 if (pS
&& pS
->GetValue() == "Standard")
1556 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject
->Lookup("K"_ostr
));
1557 CPPUNIT_ASSERT(pKids
);
1558 // one problem was that some StructElem were missing kids
1559 CPPUNIT_ASSERT(!pKids
->GetElements().empty());
1563 CPPUNIT_ASSERT_EQUAL(12, nPara
);
1566 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf152235
)
1568 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
1570 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
1571 { { "PDFUACompliance", uno::Any(true) },
1572 { "Watermark", uno::Any(u
"kendy"_ustr
) },
1573 // need to set a font to avoid assertions about missing "Helvetica"
1574 { "WatermarkFontName", uno::Any(u
"Liberation Sans"_ustr
) },
1575 { "SelectPdfVersion", uno::Any(sal_Int32(17)) } }));
1576 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1577 loadFromURL(u
"private:factory/swriter"_ustr
);
1578 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
1580 vcl::filter::PDFDocument aDocument
;
1581 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
1582 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
1584 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1585 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
1587 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1588 CPPUNIT_ASSERT(pContents
);
1589 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
1590 CPPUNIT_ASSERT(pStream
);
1591 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
1593 SvMemoryStream aUncompressed
;
1595 aZCodec
.BeginCompression();
1596 rObjectStream
.Seek(0);
1597 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1598 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1600 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
1601 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
1617 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
1622 std::string_view
const line(pStart
, pLine
- pStart
);
1624 if (!line
.empty() && line
[0] != '%')
1626 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
1627 if (o3tl::starts_with(line
, "/Artifact "))
1629 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1633 else if (o3tl::starts_with(line
, "/Standard<</MCID "))
1635 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
1639 else if (line
== "EMC")
1641 CPPUNIT_ASSERT_MESSAGE("unexpected end", state
!= Default
);
1644 else if (nLine
> 1) // first line is expected "0.1 w"
1646 CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state
!= Default
);
1650 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
1651 CPPUNIT_ASSERT_GREATEREQUAL(0, nTagged
); // text in body
1652 CPPUNIT_ASSERT_GREATEREQUAL(2, nArtifacts
); // 1 watermark + 1 other thing
1655 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf149140
)
1658 uno::Sequence
<beans::PropertyValue
> aFilterData(
1659 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1660 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1661 vcl::filter::PDFDocument aDocument
;
1662 load(u
"TableTH_test_LibreOfficeWriter7.3.3_HeaderRow-HeadersInTopRow.fodt", aDocument
);
1664 // The document has one page.
1665 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1666 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
1669 for (const auto& rDocElement
: aDocument
.GetElements())
1671 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
1674 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1675 if (pType
&& pType
->GetValue() == "StructElem")
1677 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
1678 if (pS
&& pS
->GetValue() == "TH")
1682 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject
->Lookup("A"_ostr
));
1683 CPPUNIT_ASSERT(pAttrs
!= nullptr);
1684 for (const auto& rAttrRef
: pAttrs
->GetElements())
1686 auto pAttrDict
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(rAttrRef
);
1687 CPPUNIT_ASSERT(pAttrDict
!= nullptr);
1688 auto pOwner
= dynamic_cast<vcl::filter::PDFNameElement
*>(
1689 pAttrDict
->LookupElement("O"_ostr
));
1690 CPPUNIT_ASSERT(pOwner
!= nullptr);
1691 if (pOwner
->GetValue() == "Table")
1693 auto pScope
= dynamic_cast<vcl::filter::PDFNameElement
*>(
1694 pAttrDict
->LookupElement("Scope"_ostr
));
1695 CPPUNIT_ASSERT(pScope
!= nullptr);
1696 CPPUNIT_ASSERT_EQUAL("Column"_ostr
, pScope
->GetValue());
1700 CPPUNIT_ASSERT_EQUAL(1, nTable
);
1705 CPPUNIT_ASSERT_EQUAL(6, nTH
);
1708 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testNestedSection
)
1711 uno::Sequence
<beans::PropertyValue
> aFilterData(
1712 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1713 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1715 vcl::filter::PDFDocument aDocument
;
1716 load(u
"nestedsection.fodt", aDocument
);
1718 // the assert needs 2 follows to reproduce => 3 pages
1719 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1720 CPPUNIT_ASSERT_EQUAL(size_t(3), aPages
.size());
1723 for (const auto& rDocElement
: aDocument
.GetElements())
1725 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
1728 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
1729 if (pType1
&& pType1
->GetValue() == "StructElem")
1731 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
1732 if (pS1
&& pS1
->GetValue() == "Document")
1735 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1
->Lookup("K"_ostr
));
1736 CPPUNIT_ASSERT(pKids1
);
1737 // assume there are no MCID ref at this level
1738 auto pKids1v
= pKids1
->GetElements();
1739 auto pRefKid10
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1v
[0]);
1740 CPPUNIT_ASSERT(pRefKid10
);
1741 auto pObject10
= pRefKid10
->LookupObject();
1742 CPPUNIT_ASSERT(pObject10
);
1744 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10
->Lookup("Type"_ostr
));
1745 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType10
->GetValue());
1746 auto pS10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10
->Lookup("S"_ostr
));
1747 CPPUNIT_ASSERT_EQUAL("Sect"_ostr
, pS10
->GetValue());
1750 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject10
->Lookup("K"_ostr
));
1751 CPPUNIT_ASSERT(pKids10
);
1752 // assume there are no MCID ref at this level
1753 auto pKids10v
= pKids10
->GetElements();
1755 auto pRefKid100
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids10v
[0]);
1756 CPPUNIT_ASSERT(pRefKid100
);
1757 auto pObject100
= pRefKid100
->LookupObject();
1758 CPPUNIT_ASSERT(pObject100
);
1760 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject100
->Lookup("Type"_ostr
));
1761 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType100
->GetValue());
1763 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject100
->Lookup("S"_ostr
));
1764 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS100
->GetValue());
1766 auto pRefKid101
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids10v
[1]);
1767 CPPUNIT_ASSERT(pRefKid101
);
1768 auto pObject101
= pRefKid101
->LookupObject();
1769 CPPUNIT_ASSERT(pObject101
);
1771 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject101
->Lookup("Type"_ostr
));
1772 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType101
->GetValue());
1774 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject101
->Lookup("S"_ostr
));
1775 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS101
->GetValue());
1777 auto pRefKid102
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids10v
[2]);
1778 CPPUNIT_ASSERT(pRefKid102
);
1779 auto pObject102
= pRefKid102
->LookupObject();
1780 CPPUNIT_ASSERT(pObject102
);
1782 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject102
->Lookup("Type"_ostr
));
1783 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType102
->GetValue());
1785 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject102
->Lookup("S"_ostr
));
1786 CPPUNIT_ASSERT_EQUAL("Sect"_ostr
, pS102
->GetValue());
1789 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject102
->Lookup("K"_ostr
));
1790 CPPUNIT_ASSERT(pKids102
);
1791 // assume there are no MCID ref at this level
1792 auto pKids102v
= pKids102
->GetElements();
1794 auto pRefKid1020
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids102v
[0]);
1795 CPPUNIT_ASSERT(pRefKid1020
);
1796 auto pObject1020
= pRefKid1020
->LookupObject();
1797 CPPUNIT_ASSERT(pObject1020
);
1799 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1020
->Lookup("Type"_ostr
));
1800 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1020
->GetValue());
1802 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1020
->Lookup("S"_ostr
));
1803 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS1020
->GetValue());
1805 CPPUNIT_ASSERT_EQUAL(size_t(1), pKids102v
.size());
1807 auto pRefKid103
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids10v
[3]);
1808 CPPUNIT_ASSERT(pRefKid103
);
1809 auto pObject103
= pRefKid103
->LookupObject();
1810 CPPUNIT_ASSERT(pObject103
);
1812 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject103
->Lookup("Type"_ostr
));
1813 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType103
->GetValue());
1815 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject103
->Lookup("S"_ostr
));
1816 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS103
->GetValue());
1818 auto pRefKid104
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids10v
[4]);
1819 CPPUNIT_ASSERT(pRefKid104
);
1820 auto pObject104
= pRefKid104
->LookupObject();
1821 CPPUNIT_ASSERT(pObject104
);
1823 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject104
->Lookup("Type"_ostr
));
1824 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType104
->GetValue());
1826 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject104
->Lookup("S"_ostr
));
1827 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS104
->GetValue());
1829 CPPUNIT_ASSERT_EQUAL(size_t(5), pKids10v
.size());
1831 auto pRefKid11
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1v
[1]);
1832 CPPUNIT_ASSERT(pRefKid11
);
1833 auto pObject11
= pRefKid11
->LookupObject();
1834 CPPUNIT_ASSERT(pObject11
);
1836 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("Type"_ostr
));
1837 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11
->GetValue());
1838 auto pS11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("S"_ostr
));
1839 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS11
->GetValue());
1841 CPPUNIT_ASSERT_EQUAL(size_t(2), pKids1v
.size());
1846 CPPUNIT_ASSERT_EQUAL(1, nDoc
);
1849 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf157817
)
1852 uno::Sequence
<beans::PropertyValue
> aFilterData(
1853 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
1854 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1856 vcl::filter::PDFDocument aDocument
;
1857 load(u
"SimpleTOC.fodt", aDocument
);
1859 // The document has one page.
1860 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1861 CPPUNIT_ASSERT_EQUAL(size_t(2), aPages
.size());
1863 vcl::filter::PDFObjectElement
* pTOC(nullptr);
1864 for (const auto& rDocElement
: aDocument
.GetElements())
1866 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
1869 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
1870 if (pType1
&& pType1
->GetValue() == "StructElem")
1872 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
1873 if (pS1
&& pS1
->GetValue() == "TOC")
1879 CPPUNIT_ASSERT(pTOC
);
1881 auto pKidsT
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pTOC
->Lookup("K"_ostr
));
1882 CPPUNIT_ASSERT(pKidsT
);
1883 // assume there are no MCID ref at this level
1884 auto pKidsTv
= pKidsT
->GetElements();
1885 auto pRefKidT0
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsTv
[0]);
1886 CPPUNIT_ASSERT(pRefKidT0
);
1887 auto pObjectT0
= pRefKidT0
->LookupObject();
1888 CPPUNIT_ASSERT(pObjectT0
);
1889 auto pTypeT0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT0
->Lookup("Type"_ostr
));
1890 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT0
->GetValue());
1891 auto pST0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT0
->Lookup("S"_ostr
));
1892 CPPUNIT_ASSERT_EQUAL("Caption"_ostr
, pST0
->GetValue());
1894 auto pKidsT0
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectT0
->Lookup("K"_ostr
));
1895 CPPUNIT_ASSERT(pKidsT0
);
1896 auto pKidsT0v
= pKidsT0
->GetElements();
1897 auto pRefKidT00
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsT0v
[0]);
1898 CPPUNIT_ASSERT(pRefKidT00
);
1899 auto pObjectT00
= pRefKidT00
->LookupObject();
1900 CPPUNIT_ASSERT(pObjectT00
);
1901 auto pTypeT00
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT00
->Lookup("Type"_ostr
));
1902 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT00
->GetValue());
1903 auto pST00
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT00
->Lookup("S"_ostr
));
1904 CPPUNIT_ASSERT_EQUAL("Contents#20Heading"_ostr
, pST00
->GetValue());
1906 auto pRefKidT1
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsTv
[1]);
1907 CPPUNIT_ASSERT(pRefKidT1
);
1908 auto pObjectT1
= pRefKidT1
->LookupObject();
1909 CPPUNIT_ASSERT(pObjectT1
);
1910 auto pTypeT1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT1
->Lookup("Type"_ostr
));
1911 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT1
->GetValue());
1912 auto pST1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT1
->Lookup("S"_ostr
));
1913 CPPUNIT_ASSERT_EQUAL("TOCI"_ostr
, pST1
->GetValue());
1915 auto pKidsT1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectT1
->Lookup("K"_ostr
));
1916 CPPUNIT_ASSERT(pKidsT1
);
1917 auto pKidsT1v
= pKidsT1
->GetElements();
1919 auto pRefKidT10
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsT1v
[0]);
1920 CPPUNIT_ASSERT(pRefKidT10
);
1921 auto pObjectT10
= pRefKidT10
->LookupObject();
1922 CPPUNIT_ASSERT(pObjectT10
);
1923 auto pTypeT10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT10
->Lookup("Type"_ostr
));
1924 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT10
->GetValue());
1925 auto pST10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT10
->Lookup("S"_ostr
));
1926 CPPUNIT_ASSERT_EQUAL("Contents#201"_ostr
, pST10
->GetValue());
1928 auto pKidsT10
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectT10
->Lookup("K"_ostr
));
1929 CPPUNIT_ASSERT(pKidsT10
);
1930 auto pKidsT10v
= pKidsT10
->GetElements();
1931 CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT10v
.size());
1933 // there is one and only one Link
1934 auto pRefKidT100
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsT10v
[0]);
1935 CPPUNIT_ASSERT(pRefKidT100
);
1936 auto pObjectT100
= pRefKidT100
->LookupObject();
1937 CPPUNIT_ASSERT(pObjectT100
);
1938 auto pTypeT100
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT100
->Lookup("Type"_ostr
));
1939 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT100
->GetValue());
1940 auto pST100
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT100
->Lookup("S"_ostr
));
1941 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pST100
->GetValue());
1943 auto pRefKidT2
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsTv
[1]);
1944 CPPUNIT_ASSERT(pRefKidT2
);
1945 auto pObjectT2
= pRefKidT2
->LookupObject();
1946 CPPUNIT_ASSERT(pObjectT2
);
1947 auto pTypeT2
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT2
->Lookup("Type"_ostr
));
1948 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT2
->GetValue());
1949 auto pST2
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT2
->Lookup("S"_ostr
));
1950 CPPUNIT_ASSERT_EQUAL("TOCI"_ostr
, pST2
->GetValue());
1952 auto pKidsT2
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectT2
->Lookup("K"_ostr
));
1953 CPPUNIT_ASSERT(pKidsT2
);
1954 auto pKidsT2v
= pKidsT2
->GetElements();
1956 auto pRefKidT20
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsT2v
[0]);
1957 CPPUNIT_ASSERT(pRefKidT20
);
1958 auto pObjectT20
= pRefKidT20
->LookupObject();
1959 CPPUNIT_ASSERT(pObjectT20
);
1960 auto pTypeT20
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT20
->Lookup("Type"_ostr
));
1961 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT20
->GetValue());
1962 auto pST20
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT20
->Lookup("S"_ostr
));
1963 CPPUNIT_ASSERT_EQUAL("Contents#201"_ostr
, pST20
->GetValue());
1965 auto pKidsT20
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectT20
->Lookup("K"_ostr
));
1966 CPPUNIT_ASSERT(pKidsT20
);
1967 auto pKidsT20v
= pKidsT20
->GetElements();
1968 CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT20v
.size());
1970 // there is one and only one Link
1971 auto pRefKidT200
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsT20v
[0]);
1972 CPPUNIT_ASSERT(pRefKidT200
);
1973 auto pObjectT200
= pRefKidT200
->LookupObject();
1974 CPPUNIT_ASSERT(pObjectT200
);
1975 auto pTypeT200
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT200
->Lookup("Type"_ostr
));
1976 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT200
->GetValue());
1977 auto pST200
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT200
->Lookup("S"_ostr
));
1978 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pST200
->GetValue());
1980 auto pRefKidT3
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsTv
[1]);
1981 CPPUNIT_ASSERT(pRefKidT3
);
1982 auto pObjectT3
= pRefKidT3
->LookupObject();
1983 CPPUNIT_ASSERT(pObjectT3
);
1984 auto pTypeT3
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT3
->Lookup("Type"_ostr
));
1985 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT3
->GetValue());
1986 auto pST3
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT3
->Lookup("S"_ostr
));
1987 CPPUNIT_ASSERT_EQUAL("TOCI"_ostr
, pST3
->GetValue());
1989 auto pKidsT3
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectT3
->Lookup("K"_ostr
));
1990 CPPUNIT_ASSERT(pKidsT3
);
1991 auto pKidsT3v
= pKidsT3
->GetElements();
1993 auto pRefKidT30
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsT3v
[0]);
1994 CPPUNIT_ASSERT(pRefKidT30
);
1995 auto pObjectT30
= pRefKidT30
->LookupObject();
1996 CPPUNIT_ASSERT(pObjectT30
);
1997 auto pTypeT30
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT30
->Lookup("Type"_ostr
));
1998 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT30
->GetValue());
1999 auto pST30
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT30
->Lookup("S"_ostr
));
2000 CPPUNIT_ASSERT_EQUAL("Contents#201"_ostr
, pST30
->GetValue());
2002 auto pKidsT30
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectT30
->Lookup("K"_ostr
));
2003 CPPUNIT_ASSERT(pKidsT30
);
2004 auto pKidsT30v
= pKidsT30
->GetElements();
2005 CPPUNIT_ASSERT_EQUAL(size_t(1), pKidsT30v
.size());
2007 // there is one and only one Link
2008 auto pRefKidT300
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsT30v
[0]);
2009 CPPUNIT_ASSERT(pRefKidT300
);
2010 auto pObjectT300
= pRefKidT300
->LookupObject();
2011 CPPUNIT_ASSERT(pObjectT300
);
2012 auto pTypeT300
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT300
->Lookup("Type"_ostr
));
2013 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeT300
->GetValue());
2014 auto pST300
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectT300
->Lookup("S"_ostr
));
2015 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pST300
->GetValue());
2018 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf135638
)
2021 uno::Sequence
<beans::PropertyValue
> aFilterData(
2022 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2023 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2025 vcl::filter::PDFDocument aDocument
;
2026 load(u
"image-shape.fodt", aDocument
);
2028 // The document has one page.
2029 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2030 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
2033 for (const auto& rDocElement
: aDocument
.GetElements())
2035 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2038 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
2039 if (pType
&& pType
->GetValue() == "StructElem")
2041 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
2042 if (pS
&& pS
->GetValue() == "Figure")
2045 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pObject
->Lookup("A"_ostr
));
2046 CPPUNIT_ASSERT(pAttrDict
!= nullptr);
2047 auto pOwner
= dynamic_cast<vcl::filter::PDFNameElement
*>(
2048 pAttrDict
->LookupElement("O"_ostr
));
2049 CPPUNIT_ASSERT(pOwner
!= nullptr);
2050 CPPUNIT_ASSERT_EQUAL("Layout"_ostr
, pOwner
->GetValue());
2051 auto pBBox
= dynamic_cast<vcl::filter::PDFArrayElement
*>(
2052 pAttrDict
->LookupElement("BBox"_ostr
));
2053 CPPUNIT_ASSERT(pBBox
!= nullptr);
2056 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2058 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[0])
2061 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2063 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[1])
2066 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2068 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[2])
2071 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2073 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[3])
2079 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2081 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[0])
2084 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2086 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[1])
2089 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2091 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[2])
2094 CPPUNIT_ASSERT_DOUBLES_EQUAL(
2096 dynamic_cast<vcl::filter::PDFNumberElement
*>(pBBox
->GetElements()[3])
2104 // the first one is a Writer image, 2nd one SdrRectObj
2105 CPPUNIT_ASSERT_EQUAL(2, nFigure
);
2108 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf157703
)
2111 uno::Sequence
<beans::PropertyValue
> aFilterData(
2112 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2113 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2115 vcl::filter::PDFDocument aDocument
;
2116 load(u
"LO_Lbl_Lbody_bug_report.fodt", aDocument
);
2118 // The document has one page.
2119 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2120 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
2122 vcl::filter::PDFObjectElement
* pDocument(nullptr);
2123 for (const auto& rDocElement
: aDocument
.GetElements())
2125 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2128 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
2129 if (pType1
&& pType1
->GetValue() == "StructElem")
2131 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
2132 if (pS1
&& pS1
->GetValue() == "Document")
2134 pDocument
= pObject1
;
2138 CPPUNIT_ASSERT(pDocument
);
2140 auto pKidsD
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pDocument
->Lookup("K"_ostr
));
2141 CPPUNIT_ASSERT(pKidsD
);
2142 // assume there are no MCID ref at this level
2143 auto pKidsDv
= pKidsD
->GetElements();
2144 auto pRefKidD0
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsDv
[0]);
2145 CPPUNIT_ASSERT(pRefKidD0
);
2146 auto pObjectD0
= pRefKidD0
->LookupObject();
2147 CPPUNIT_ASSERT(pObjectD0
);
2148 auto pTypeD0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD0
->Lookup("Type"_ostr
));
2149 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD0
->GetValue());
2150 auto pSD0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD0
->Lookup("S"_ostr
));
2151 CPPUNIT_ASSERT_EQUAL("H1"_ostr
, pSD0
->GetValue());
2153 auto pKidsD0
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD0
->Lookup("K"_ostr
));
2154 CPPUNIT_ASSERT(pKidsD0
);
2155 auto pKidsD0v
= pKidsD0
->GetElements();
2156 auto pRefKidD00
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[0]);
2158 CPPUNIT_ASSERT(!pRefKidD00
);
2161 auto pRefKidD01
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[1]);
2162 CPPUNIT_ASSERT(!pRefKidD01
);
2164 auto pRefKidD1
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsDv
[1]);
2165 CPPUNIT_ASSERT(pRefKidD1
);
2166 auto pObjectD1
= pRefKidD1
->LookupObject();
2167 CPPUNIT_ASSERT(pObjectD1
);
2168 auto pTypeD1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD1
->Lookup("Type"_ostr
));
2169 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD1
->GetValue());
2170 auto pSD1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD1
->Lookup("S"_ostr
));
2171 CPPUNIT_ASSERT_EQUAL("H2"_ostr
, pSD1
->GetValue());
2173 auto pKidsD1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD1
->Lookup("K"_ostr
));
2174 CPPUNIT_ASSERT(pKidsD1
);
2175 auto pKidsD1v
= pKidsD1
->GetElements();
2178 auto pRefKidD11
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD1v
[0]);
2179 CPPUNIT_ASSERT(!pRefKidD11
);
2182 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testSpans
)
2185 uno::Sequence
<beans::PropertyValue
> aFilterData(
2186 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2187 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2189 vcl::filter::PDFDocument aDocument
;
2190 load(u
"spanlist.fodt", aDocument
);
2192 // The document has two pages.
2193 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2194 CPPUNIT_ASSERT_EQUAL(size_t(2), aPages
.size());
2197 for (const auto& rDocElement
: aDocument
.GetElements())
2199 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2202 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
2203 if (pType1
&& pType1
->GetValue() == "StructElem")
2205 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
2206 if (pS1
&& pS1
->GetValue() == "Document")
2209 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1
->Lookup("K"_ostr
));
2210 CPPUNIT_ASSERT(pKids1
);
2211 // assume there are no MCID ref at this level
2212 auto vKids1
= pKids1
->GetElements();
2213 CPPUNIT_ASSERT_EQUAL(size_t(2), vKids1
.size());
2214 auto pRefKid10
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids1
[0]);
2215 CPPUNIT_ASSERT(pRefKid10
);
2216 auto pObject10
= pRefKid10
->LookupObject();
2217 CPPUNIT_ASSERT(pObject10
);
2219 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10
->Lookup("Type"_ostr
));
2220 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType10
->GetValue());
2221 auto pS10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10
->Lookup("S"_ostr
));
2222 CPPUNIT_ASSERT_EQUAL("L"_ostr
, pS10
->GetValue());
2225 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject10
->Lookup("K"_ostr
));
2226 CPPUNIT_ASSERT(pKids10
);
2227 // assume there are no MCID ref at this level
2228 auto vKids10
= pKids10
->GetElements();
2229 CPPUNIT_ASSERT_EQUAL(size_t(4), vKids10
.size());
2231 auto pRefKid100
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10
[0]);
2232 CPPUNIT_ASSERT(pRefKid100
);
2233 auto pObject100
= pRefKid100
->LookupObject();
2234 CPPUNIT_ASSERT(pObject100
);
2236 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject100
->Lookup("Type"_ostr
));
2237 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType100
->GetValue());
2239 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject100
->Lookup("S"_ostr
));
2240 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS100
->GetValue());
2243 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject100
->Lookup("K"_ostr
));
2244 CPPUNIT_ASSERT(pKids100
);
2245 // assume there are no MCID ref at this level
2246 auto vKids100
= pKids100
->GetElements();
2247 CPPUNIT_ASSERT_EQUAL(size_t(2), vKids100
.size());
2249 auto pRefKid1000
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids100
[0]);
2250 CPPUNIT_ASSERT(pRefKid1000
);
2251 auto pObject1000
= pRefKid1000
->LookupObject();
2252 CPPUNIT_ASSERT(pObject1000
);
2254 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1000
->Lookup("Type"_ostr
));
2255 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1000
->GetValue());
2257 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1000
->Lookup("S"_ostr
));
2258 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS1000
->GetValue());
2260 auto pRefKid1001
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids100
[1]);
2261 CPPUNIT_ASSERT(pRefKid1001
);
2262 auto pObject1001
= pRefKid1001
->LookupObject();
2263 CPPUNIT_ASSERT(pObject1001
);
2265 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1001
->Lookup("Type"_ostr
));
2266 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1001
->GetValue());
2268 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1001
->Lookup("S"_ostr
));
2269 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS1001
->GetValue());
2271 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1001
->Lookup("K"_ostr
));
2272 CPPUNIT_ASSERT(pKids1001
);
2273 // assume there are no MCID ref at this level
2274 auto vKids1001
= pKids1001
->GetElements();
2275 CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1001
.size());
2277 auto pRefKid10010
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids1001
[0]);
2278 CPPUNIT_ASSERT(pRefKid10010
);
2279 auto pObject10010
= pRefKid10010
->LookupObject();
2280 CPPUNIT_ASSERT(pObject10010
);
2282 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10010
->Lookup("Type"_ostr
));
2283 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType10010
->GetValue());
2285 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10010
->Lookup("S"_ostr
));
2286 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS10010
->GetValue());
2288 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject10010
->Lookup("K"_ostr
));
2289 CPPUNIT_ASSERT(pKids10010
);
2290 // assume there are no MCID ref at this level
2291 auto vKids10010
= pKids10010
->GetElements();
2293 CPPUNIT_ASSERT_EQUAL(size_t(1), vKids10010
.size());
2295 auto pRefKid100100
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10010
[0]);
2296 CPPUNIT_ASSERT(pRefKid100100
);
2297 auto pObject100100
= pRefKid100100
->LookupObject();
2298 CPPUNIT_ASSERT(pObject100100
);
2299 auto pType100100
= dynamic_cast<vcl::filter::PDFNameElement
*>(
2300 pObject100100
->Lookup("Type"_ostr
));
2301 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType100100
->GetValue());
2303 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject100100
->Lookup("S"_ostr
));
2304 CPPUNIT_ASSERT_EQUAL("Span"_ostr
, pS100100
->GetValue());
2305 // this span exists because of lang
2306 auto pLang100100
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
2307 pObject100100
->Lookup("Lang"_ostr
));
2308 CPPUNIT_ASSERT_EQUAL("en-GB"_ostr
, pLang100100
->GetValue());
2310 auto pRefKid101
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10
[1]);
2311 CPPUNIT_ASSERT(pRefKid101
);
2312 auto pObject101
= pRefKid101
->LookupObject();
2313 CPPUNIT_ASSERT(pObject101
);
2315 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject101
->Lookup("Type"_ostr
));
2316 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType101
->GetValue());
2318 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject101
->Lookup("S"_ostr
));
2319 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS101
->GetValue());
2322 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject101
->Lookup("K"_ostr
));
2323 CPPUNIT_ASSERT(pKids101
);
2324 // assume there are no MCID ref at this level
2325 auto vKids101
= pKids101
->GetElements();
2326 CPPUNIT_ASSERT_EQUAL(size_t(2), vKids101
.size());
2328 auto pRefKid1010
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids101
[0]);
2329 CPPUNIT_ASSERT(pRefKid1010
);
2330 auto pObject1010
= pRefKid1010
->LookupObject();
2331 CPPUNIT_ASSERT(pObject1010
);
2333 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1010
->Lookup("Type"_ostr
));
2334 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1010
->GetValue());
2336 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1010
->Lookup("S"_ostr
));
2337 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS1010
->GetValue());
2339 auto pRefKid1011
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids101
[1]);
2340 CPPUNIT_ASSERT(pRefKid1011
);
2341 auto pObject1011
= pRefKid1011
->LookupObject();
2342 CPPUNIT_ASSERT(pObject1011
);
2344 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1011
->Lookup("Type"_ostr
));
2345 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1011
->GetValue());
2347 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1011
->Lookup("S"_ostr
));
2348 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS1011
->GetValue());
2351 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1011
->Lookup("K"_ostr
));
2352 CPPUNIT_ASSERT(pKids1011
);
2353 // assume there are no MCID ref at this level
2354 auto vKids1011
= pKids1011
->GetElements();
2355 CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1011
.size());
2357 auto pRefKid10110
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids1011
[0]);
2358 CPPUNIT_ASSERT(pRefKid10110
);
2359 auto pObject10110
= pRefKid10110
->LookupObject();
2360 CPPUNIT_ASSERT(pObject10110
);
2362 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10110
->Lookup("Type"_ostr
));
2363 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType10110
->GetValue());
2365 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10110
->Lookup("S"_ostr
));
2366 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS10110
->GetValue());
2368 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject10110
->Lookup("K"_ostr
));
2369 CPPUNIT_ASSERT(pKids10110
);
2370 auto vKids10110
= pKids10110
->GetElements();
2371 // only MCIDs, no span
2372 for (size_t i
= 0; i
< vKids10110
.size(); ++i
)
2374 auto pKid
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10110
[i
]);
2375 CPPUNIT_ASSERT(!pKid
);
2378 auto pRefKid102
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10
[2]);
2379 CPPUNIT_ASSERT(pRefKid102
);
2380 auto pObject102
= pRefKid102
->LookupObject();
2381 CPPUNIT_ASSERT(pObject102
);
2383 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject102
->Lookup("Type"_ostr
));
2384 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType102
->GetValue());
2386 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject102
->Lookup("S"_ostr
));
2387 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS102
->GetValue());
2390 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject102
->Lookup("K"_ostr
));
2391 CPPUNIT_ASSERT(pKids102
);
2392 // assume there are no MCID ref at this level
2393 auto vKids102
= pKids102
->GetElements();
2394 CPPUNIT_ASSERT_EQUAL(size_t(2), vKids102
.size());
2396 auto pRefKid1020
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids102
[0]);
2397 CPPUNIT_ASSERT(pRefKid1020
);
2398 auto pObject1020
= pRefKid1020
->LookupObject();
2399 CPPUNIT_ASSERT(pObject1020
);
2401 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1020
->Lookup("Type"_ostr
));
2402 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1020
->GetValue());
2404 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1020
->Lookup("S"_ostr
));
2405 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS1020
->GetValue());
2407 auto pRefKid1021
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids102
[1]);
2408 CPPUNIT_ASSERT(pRefKid1021
);
2409 auto pObject1021
= pRefKid1021
->LookupObject();
2410 CPPUNIT_ASSERT(pObject1021
);
2412 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1021
->Lookup("Type"_ostr
));
2413 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1021
->GetValue());
2415 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1021
->Lookup("S"_ostr
));
2416 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS1021
->GetValue());
2419 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1021
->Lookup("K"_ostr
));
2420 CPPUNIT_ASSERT(pKids1021
);
2421 // assume there are no MCID ref at this level
2422 auto vKids1021
= pKids1021
->GetElements();
2423 CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1021
.size());
2425 auto pRefKid10210
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids1021
[0]);
2426 CPPUNIT_ASSERT(pRefKid10210
);
2427 auto pObject10210
= pRefKid10210
->LookupObject();
2428 CPPUNIT_ASSERT(pObject10210
);
2430 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10210
->Lookup("Type"_ostr
));
2431 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType10210
->GetValue());
2433 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10210
->Lookup("S"_ostr
));
2434 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS10210
->GetValue());
2436 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject10210
->Lookup("K"_ostr
));
2437 CPPUNIT_ASSERT(pKids10210
);
2438 // assume there are no MCID ref at this level
2439 auto vKids10210
= pKids10210
->GetElements();
2440 // 2 span and a hyperlink
2441 CPPUNIT_ASSERT_EQUAL(size_t(3), vKids10210
.size());
2443 auto pRefKid102100
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10210
[0]);
2444 CPPUNIT_ASSERT(pRefKid102100
);
2445 auto pObject102100
= pRefKid102100
->LookupObject();
2446 CPPUNIT_ASSERT(pObject102100
);
2447 auto pType102100
= dynamic_cast<vcl::filter::PDFNameElement
*>(
2448 pObject102100
->Lookup("Type"_ostr
));
2449 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType102100
->GetValue());
2451 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject102100
->Lookup("S"_ostr
));
2452 CPPUNIT_ASSERT_EQUAL("Span"_ostr
, pS102100
->GetValue());
2454 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject102100
->Lookup("K"_ostr
));
2455 CPPUNIT_ASSERT(pKids102100
);
2456 auto vKids102100
= pKids102100
->GetElements();
2457 for (size_t i
= 0; i
< vKids102100
.size(); ++i
)
2459 auto pKid
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids102100
[i
]);
2460 CPPUNIT_ASSERT(!pKid
);
2463 auto pRefKid102101
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10210
[1]);
2464 CPPUNIT_ASSERT(pRefKid102101
);
2465 auto pObject102101
= pRefKid102101
->LookupObject();
2466 CPPUNIT_ASSERT(pObject102101
);
2467 auto pType102101
= dynamic_cast<vcl::filter::PDFNameElement
*>(
2468 pObject102101
->Lookup("Type"_ostr
));
2469 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType102101
->GetValue());
2471 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject102101
->Lookup("S"_ostr
));
2472 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pS102101
->GetValue());
2474 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject102101
->Lookup("K"_ostr
));
2475 CPPUNIT_ASSERT(pKids102101
);
2476 auto vKids102101
= pKids102101
->GetElements();
2478 for (size_t i
= 0; i
< vKids102101
.size(); ++i
)
2480 auto pKid
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(vKids102101
[i
]);
2483 ++nRef
; // annotation
2486 CPPUNIT_ASSERT_EQUAL(1, nRef
);
2488 auto pRefKid102102
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10210
[2]);
2489 CPPUNIT_ASSERT(pRefKid102102
);
2490 auto pObject102102
= pRefKid102102
->LookupObject();
2491 CPPUNIT_ASSERT(pObject102102
);
2492 auto pType102102
= dynamic_cast<vcl::filter::PDFNameElement
*>(
2493 pObject102102
->Lookup("Type"_ostr
));
2494 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType102102
->GetValue());
2496 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject102102
->Lookup("S"_ostr
));
2497 CPPUNIT_ASSERT_EQUAL("Span"_ostr
, pS102102
->GetValue());
2499 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject102102
->Lookup("K"_ostr
));
2500 CPPUNIT_ASSERT(pKids102102
);
2501 auto vKids102102
= pKids102102
->GetElements();
2502 // there is a footnote
2504 for (size_t i
= 0; i
< vKids102102
.size(); ++i
)
2506 auto pKid
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids102102
[i
]);
2509 auto pObject
= pKid
->LookupObject();
2510 CPPUNIT_ASSERT(pObject
);
2511 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(
2512 pObject
->Lookup("Type"_ostr
));
2513 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType
->GetValue());
2515 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
2516 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pS
->GetValue());
2520 CPPUNIT_ASSERT_EQUAL(1, nFtn
);
2522 auto pRefKid103
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10
[3]);
2523 CPPUNIT_ASSERT(pRefKid103
);
2524 auto pObject103
= pRefKid103
->LookupObject();
2525 CPPUNIT_ASSERT(pObject103
);
2527 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject103
->Lookup("Type"_ostr
));
2528 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType103
->GetValue());
2530 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject103
->Lookup("S"_ostr
));
2531 CPPUNIT_ASSERT_EQUAL("LI"_ostr
, pS103
->GetValue());
2534 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject103
->Lookup("K"_ostr
));
2535 CPPUNIT_ASSERT(pKids103
);
2536 // assume there are no MCID ref at this level
2537 auto vKids103
= pKids103
->GetElements();
2538 CPPUNIT_ASSERT_EQUAL(size_t(2), vKids103
.size());
2540 auto pRefKid1030
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids103
[0]);
2541 CPPUNIT_ASSERT(pRefKid1030
);
2542 auto pObject1030
= pRefKid1030
->LookupObject();
2543 CPPUNIT_ASSERT(pObject1030
);
2545 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1030
->Lookup("Type"_ostr
));
2546 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1030
->GetValue());
2548 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1030
->Lookup("S"_ostr
));
2549 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS1030
->GetValue());
2551 auto pRefKid1031
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids103
[1]);
2552 CPPUNIT_ASSERT(pRefKid1031
);
2553 auto pObject1031
= pRefKid1031
->LookupObject();
2554 CPPUNIT_ASSERT(pObject1031
);
2556 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1031
->Lookup("Type"_ostr
));
2557 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1031
->GetValue());
2559 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1031
->Lookup("S"_ostr
));
2560 CPPUNIT_ASSERT_EQUAL("LBody"_ostr
, pS1031
->GetValue());
2563 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1031
->Lookup("K"_ostr
));
2564 CPPUNIT_ASSERT(pKids1031
);
2565 // assume there are no MCID ref at this level
2566 auto vKids1031
= pKids1031
->GetElements();
2567 CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1031
.size());
2569 auto pRefKid10310
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids1031
[0]);
2570 CPPUNIT_ASSERT(pRefKid10310
);
2571 auto pObject10310
= pRefKid10310
->LookupObject();
2572 CPPUNIT_ASSERT(pObject10310
);
2574 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10310
->Lookup("Type"_ostr
));
2575 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType10310
->GetValue());
2577 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject10310
->Lookup("S"_ostr
));
2578 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pS10310
->GetValue());
2580 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject10310
->Lookup("K"_ostr
));
2581 CPPUNIT_ASSERT(pKids10310
);
2582 // assume there are no MCID ref at this level
2583 auto vKids10310
= pKids10310
->GetElements();
2584 // only one span, following a MCID for some strike-out gap
2585 CPPUNIT_ASSERT_EQUAL(size_t(2), vKids10310
.size());
2587 auto pRefKid103100
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10310
[0]);
2588 CPPUNIT_ASSERT(!pRefKid103100
);
2590 auto pRefKid103101
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids10310
[1]);
2591 CPPUNIT_ASSERT(pRefKid103101
);
2592 auto pObject103101
= pRefKid103101
->LookupObject();
2593 CPPUNIT_ASSERT(pObject103101
);
2594 auto pType103101
= dynamic_cast<vcl::filter::PDFNameElement
*>(
2595 pObject103101
->Lookup("Type"_ostr
));
2596 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType103101
->GetValue());
2598 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject103101
->Lookup("S"_ostr
));
2599 CPPUNIT_ASSERT_EQUAL("Span"_ostr
, pS103101
->GetValue());
2600 auto pDictA103101
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
2601 pObject103101
->Lookup("A"_ostr
));
2602 CPPUNIT_ASSERT(pDictA103101
!= nullptr);
2603 CPPUNIT_ASSERT_EQUAL("Layout"_ostr
, dynamic_cast<vcl::filter::PDFNameElement
*>(
2604 pDictA103101
->LookupElement("O"_ostr
))
2606 CPPUNIT_ASSERT_EQUAL("LineThrough"_ostr
,
2607 dynamic_cast<vcl::filter::PDFNameElement
*>(
2608 pDictA103101
->LookupElement("TextDecorationType"_ostr
))
2611 // now the footnote container - following the list
2612 auto pRefKid11
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids1
[1]);
2613 CPPUNIT_ASSERT(pRefKid11
);
2614 auto pObject11
= pRefKid11
->LookupObject();
2615 CPPUNIT_ASSERT(pObject11
);
2617 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("Type"_ostr
));
2618 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11
->GetValue());
2619 auto pS11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11
->Lookup("S"_ostr
));
2620 CPPUNIT_ASSERT_EQUAL("Div"_ostr
, pS11
->GetValue());
2623 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject11
->Lookup("K"_ostr
));
2624 CPPUNIT_ASSERT(pKids11
);
2625 // assume there are no MCID ref at this level
2626 auto vKids11
= pKids11
->GetElements();
2627 CPPUNIT_ASSERT_EQUAL(size_t(1), vKids11
.size());
2629 auto pRefKid110
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids11
[0]);
2630 CPPUNIT_ASSERT(pRefKid110
);
2631 auto pObject110
= pRefKid110
->LookupObject();
2632 CPPUNIT_ASSERT(pObject110
);
2634 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject110
->Lookup("Type"_ostr
));
2635 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType110
->GetValue());
2637 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject110
->Lookup("S"_ostr
));
2638 CPPUNIT_ASSERT_EQUAL("Note"_ostr
, pS110
->GetValue());
2641 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject110
->Lookup("K"_ostr
));
2642 CPPUNIT_ASSERT(pKids110
);
2643 // assume there are no MCID ref at this level
2644 auto vKids110
= pKids110
->GetElements();
2645 CPPUNIT_ASSERT_EQUAL(size_t(2), vKids110
.size());
2647 auto pRefKid1100
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids110
[0]);
2648 CPPUNIT_ASSERT(pRefKid1100
);
2649 auto pObject1100
= pRefKid1100
->LookupObject();
2650 CPPUNIT_ASSERT(pObject1100
);
2652 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1100
->Lookup("Type"_ostr
));
2653 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1100
->GetValue());
2655 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1100
->Lookup("S"_ostr
));
2656 CPPUNIT_ASSERT_EQUAL("Lbl"_ostr
, pS1100
->GetValue());
2659 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1100
->Lookup("K"_ostr
));
2660 CPPUNIT_ASSERT(pKids1100
);
2661 // assume there are no MCID ref at this level
2662 auto vKids1100
= pKids1100
->GetElements();
2663 CPPUNIT_ASSERT_EQUAL(size_t(1), vKids1100
.size());
2665 auto pRefKid11000
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids1100
[0]);
2666 CPPUNIT_ASSERT(pRefKid11000
);
2667 auto pObject11000
= pRefKid11000
->LookupObject();
2668 CPPUNIT_ASSERT(pObject11000
);
2670 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11000
->Lookup("Type"_ostr
));
2671 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType11000
->GetValue());
2673 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject11000
->Lookup("S"_ostr
));
2674 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pS11000
->GetValue());
2676 auto pRefKid1101
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(vKids110
[1]);
2677 CPPUNIT_ASSERT(pRefKid1101
);
2678 auto pObject1101
= pRefKid1101
->LookupObject();
2679 CPPUNIT_ASSERT(pObject1101
);
2681 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1101
->Lookup("Type"_ostr
));
2682 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType1101
->GetValue());
2684 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1101
->Lookup("S"_ostr
));
2685 CPPUNIT_ASSERT_EQUAL("Footnote"_ostr
, pS1101
->GetValue());
2691 CPPUNIT_ASSERT_EQUAL(1, nDoc
);
2694 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf157182
)
2696 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
2697 { "PDFUACompliance", uno::Any(true) },
2698 // only happens with PDF/A-1
2699 { "SelectPdfVersion", uno::Any(static_cast<sal_Int32
>(1)) },
2701 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2703 saveAsPDF(u
"transparentshape.fodp");
2705 // just check this does not crash or assert
2708 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf57423
)
2711 uno::Sequence
<beans::PropertyValue
> aFilterData(
2712 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2713 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2715 vcl::filter::PDFDocument aDocument
;
2716 load(u
"Description PDF Export test .odt", aDocument
);
2718 // The document has one page.
2719 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2720 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
2725 for (const auto& rDocElement
: aDocument
.GetElements())
2727 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2730 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
2731 if (pType
&& pType
->GetValue() == "StructElem")
2733 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
2734 if (pS
&& pS
->GetValue() == "Figure")
2739 CPPUNIT_ASSERT_EQUAL(u
"QR Code - Tells how to get to Mosegaard"_ustr
,
2740 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2741 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2742 pObject
->Lookup("Alt"_ostr
))));
2745 CPPUNIT_ASSERT_EQUAL(u
"Title: Arrows - Description: Explains the "
2746 u
"different arrow appearances"_ustr
,
2747 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2748 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2749 pObject
->Lookup("Alt"_ostr
))));
2752 CPPUNIT_ASSERT_EQUAL(
2753 u
"My blue triangle - Does not need further description"_ustr
,
2754 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2755 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2756 pObject
->Lookup("Alt"_ostr
))));
2761 if (pS
&& pS
->GetValue() == "Formula")
2763 CPPUNIT_ASSERT_EQUAL(
2764 u
"Equation 1 - Now we give the full description of eq 1 here"_ustr
,
2765 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2766 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2767 pObject
->Lookup("Alt"_ostr
))));
2770 if (pS
&& pS
->GetValue() == "Div")
2775 CPPUNIT_ASSERT_EQUAL(u
"This frame has a description"_ustr
,
2776 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2777 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2778 pObject
->Lookup("Alt"_ostr
))));
2781 // no properties set on this
2782 CPPUNIT_ASSERT(!pObject
->Lookup("Alt"_ostr
));
2785 CPPUNIT_ASSERT_EQUAL(u
"My textbox - Has a light background"_ustr
,
2786 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2787 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2788 pObject
->Lookup("Alt"_ostr
))));
2791 CPPUNIT_ASSERT_EQUAL(u
"Hey! There is no alternate text for Frame "
2792 u
"// but maybe not needed?"_ustr
,
2793 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2794 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2795 pObject
->Lookup("Alt"_ostr
))));
2802 CPPUNIT_ASSERT_EQUAL(3, nFigure
);
2803 CPPUNIT_ASSERT_EQUAL(1, nFormula
);
2804 CPPUNIT_ASSERT_EQUAL(4, nDiv
);
2807 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf154982
)
2810 uno::Sequence
<beans::PropertyValue
> aFilterData(
2811 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2812 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2814 vcl::filter::PDFDocument aDocument
;
2815 load(u
"tdf154982.odt", aDocument
);
2817 // The document has one page.
2818 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2819 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
2822 for (const auto& rDocElement
: aDocument
.GetElements())
2824 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2827 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
2828 if (pType
&& pType
->GetValue() == "StructElem")
2830 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
2831 if (pS
&& pS
->GetValue() == "Figure")
2836 CPPUNIT_ASSERT_EQUAL(u
"Here comes the signature - Please sign here"_ustr
,
2837 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2838 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2839 pObject
->Lookup("Alt"_ostr
))));
2842 CPPUNIT_ASSERT_EQUAL(u
"Home"_ustr
,
2843 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
2844 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2845 pObject
->Lookup("Alt"_ostr
))));
2849 // the problem was that the figures in the hell layer were not
2850 // below their anchor paragraphs in the structure tree
2852 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pObject
->Lookup("P"_ostr
));
2853 CPPUNIT_ASSERT(pParentRef
);
2854 auto pParent(pParentRef
->LookupObject());
2855 CPPUNIT_ASSERT(pParent
);
2857 = dynamic_cast<vcl::filter::PDFNameElement
*>(pParent
->Lookup("Type"_ostr
));
2858 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pParentType
->GetValue());
2860 = dynamic_cast<vcl::filter::PDFNameElement
*>(pParent
->Lookup("S"_ostr
));
2861 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pParentS
->GetValue());
2864 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pParent
->Lookup("P"_ostr
));
2865 CPPUNIT_ASSERT(pPParentRef
);
2866 auto pPParent(pPParentRef
->LookupObject());
2867 CPPUNIT_ASSERT(pPParent
);
2869 = dynamic_cast<vcl::filter::PDFNameElement
*>(pPParent
->Lookup("Type"_ostr
));
2870 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pPParentType
->GetValue());
2872 = dynamic_cast<vcl::filter::PDFNameElement
*>(pPParent
->Lookup("S"_ostr
));
2873 CPPUNIT_ASSERT_EQUAL("Document"_ostr
, pPParentS
->GetValue());
2878 CPPUNIT_ASSERT_EQUAL(2, nFigure
);
2881 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf157397
)
2884 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
2885 { "PDFUACompliance", uno::Any(true) },
2886 { "ExportFormFields", uno::Any(true) },
2888 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2890 vcl::filter::PDFDocument aDocument
;
2891 load(u
"PDF_export_with_formcontrol.fodt", aDocument
);
2893 // The document has one page.
2894 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2895 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
2897 vcl::filter::PDFObjectElement
* pDocument(nullptr);
2898 for (const auto& rDocElement
: aDocument
.GetElements())
2900 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2903 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
2904 if (pType1
&& pType1
->GetValue() == "StructElem")
2906 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
2907 if (pS1
&& pS1
->GetValue() == "Document")
2909 pDocument
= pObject1
;
2913 CPPUNIT_ASSERT(pDocument
);
2915 auto pKids1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pDocument
->Lookup("K"_ostr
));
2916 CPPUNIT_ASSERT(pKids1
);
2917 // assume there are no MCID ref at this level
2918 auto pKids1v
= pKids1
->GetElements();
2919 auto pRefKid12
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1v
[2]);
2920 CPPUNIT_ASSERT(pRefKid12
);
2921 auto pObject12
= pRefKid12
->LookupObject();
2922 CPPUNIT_ASSERT(pObject12
);
2923 auto pType12
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject12
->Lookup("Type"_ostr
));
2924 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType12
->GetValue());
2925 auto pS12
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject12
->Lookup("S"_ostr
));
2926 CPPUNIT_ASSERT_EQUAL("Text#20body"_ostr
, pS12
->GetValue());
2928 auto pKids12
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject12
->Lookup("K"_ostr
));
2929 CPPUNIT_ASSERT(pKids12
);
2930 // assume there are no MCID ref at this level
2931 auto pKids12v
= pKids12
->GetElements();
2932 auto pRefKid120
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids12v
[0]);
2933 CPPUNIT_ASSERT(pRefKid120
);
2934 auto pObject120
= pRefKid120
->LookupObject();
2935 CPPUNIT_ASSERT(pObject120
);
2936 auto pType120
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject120
->Lookup("Type"_ostr
));
2937 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType120
->GetValue());
2938 auto pS120
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject120
->Lookup("S"_ostr
));
2939 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pS120
->GetValue());
2942 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject120
->Lookup("K"_ostr
));
2945 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2947 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2948 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2957 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2958 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2959 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2960 pObjR
->LookupElement("Obj"_ostr
));
2961 auto pAnnot
= pAnnotRef
->LookupObject();
2963 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2964 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2966 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2967 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2968 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2969 pAnnot
->Lookup("Contents"_ostr
));
2970 CPPUNIT_ASSERT_EQUAL(
2971 u
"https://klexikon.zum.de/wiki/Kläranlage"_ustr
,
2972 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2973 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2975 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAnnot
->Lookup("A"_ostr
));
2976 CPPUNIT_ASSERT(pAA
);
2978 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAA
->LookupElement("Type"_ostr
));
2979 CPPUNIT_ASSERT_EQUAL("Action"_ostr
, pAAType
->GetValue());
2981 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAA
->LookupElement("S"_ostr
));
2982 CPPUNIT_ASSERT_EQUAL("URI"_ostr
, pAAS
->GetValue());
2983 auto pAAURI
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
2984 pAA
->LookupElement("URI"_ostr
));
2985 CPPUNIT_ASSERT_EQUAL("https://klexikon.zum.de/wiki/Kl%C3%A4ranlage"_ostr
,
2986 pAAURI
->GetValue());
2989 CPPUNIT_ASSERT_EQUAL(1, nMCID
);
2990 CPPUNIT_ASSERT_EQUAL(1, nRef
);
2993 auto pRefKid13
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1v
[3]);
2994 CPPUNIT_ASSERT(pRefKid13
);
2995 auto pObject13
= pRefKid13
->LookupObject();
2996 CPPUNIT_ASSERT(pObject13
);
2997 auto pType13
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject13
->Lookup("Type"_ostr
));
2998 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType13
->GetValue());
2999 auto pS13
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject13
->Lookup("S"_ostr
));
3000 CPPUNIT_ASSERT_EQUAL("Text#20body"_ostr
, pS13
->GetValue());
3002 auto pKids13
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject13
->Lookup("K"_ostr
));
3003 CPPUNIT_ASSERT(pKids13
);
3004 // assume there are no MCID ref at this level
3005 auto pKids13v
= pKids13
->GetElements();
3006 auto pRefKid130
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids13v
[0]);
3007 CPPUNIT_ASSERT(pRefKid130
);
3008 auto pObject130
= pRefKid130
->LookupObject();
3009 CPPUNIT_ASSERT(pObject130
);
3010 auto pType130
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject130
->Lookup("Type"_ostr
));
3011 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType130
->GetValue());
3012 auto pS130
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject130
->Lookup("S"_ostr
));
3013 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pS130
->GetValue());
3016 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject130
->Lookup("K"_ostr
));
3019 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3021 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3022 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3031 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3032 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3033 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3034 pObjR
->LookupElement("Obj"_ostr
));
3035 auto pAnnot
= pAnnotRef
->LookupObject();
3037 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3038 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3040 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3041 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3042 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3043 pAnnot
->Lookup("Contents"_ostr
));
3044 CPPUNIT_ASSERT_EQUAL(
3045 u
"https://de.wikipedia.org/wiki/Kläranlage#Mechanische_Vorreinigung"_ustr
,
3046 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3047 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3049 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAnnot
->Lookup("A"_ostr
));
3050 CPPUNIT_ASSERT(pAA
);
3052 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAA
->LookupElement("Type"_ostr
));
3053 CPPUNIT_ASSERT_EQUAL("Action"_ostr
, pAAType
->GetValue());
3055 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAA
->LookupElement("S"_ostr
));
3056 CPPUNIT_ASSERT_EQUAL("URI"_ostr
, pAAS
->GetValue());
3057 auto pAAURI
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
3058 pAA
->LookupElement("URI"_ostr
));
3059 CPPUNIT_ASSERT_EQUAL(
3060 "https://de.wikipedia.org/wiki/Kl%C3%A4ranlage#Mechanische_Vorreinigung"_ostr
,
3061 pAAURI
->GetValue());
3064 CPPUNIT_ASSERT_EQUAL(1, nMCID
);
3065 CPPUNIT_ASSERT_EQUAL(1, nRef
);
3068 auto pRefKid14
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1v
[4]);
3069 CPPUNIT_ASSERT(pRefKid14
);
3070 auto pObject14
= pRefKid14
->LookupObject();
3071 CPPUNIT_ASSERT(pObject14
);
3072 auto pType14
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject14
->Lookup("Type"_ostr
));
3073 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType14
->GetValue());
3074 auto pS14
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject14
->Lookup("S"_ostr
));
3075 CPPUNIT_ASSERT_EQUAL("Text#20body"_ostr
, pS14
->GetValue());
3077 auto pKids14
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject14
->Lookup("K"_ostr
));
3078 CPPUNIT_ASSERT(pKids14
);
3079 // assume there are no MCID ref at this level
3080 auto pKids14v
= pKids14
->GetElements();
3081 auto pRefKid140
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids14v
[0]);
3082 CPPUNIT_ASSERT(pRefKid140
);
3083 auto pObject140
= pRefKid140
->LookupObject();
3084 CPPUNIT_ASSERT(pObject140
);
3085 auto pType140
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject140
->Lookup("Type"_ostr
));
3086 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType140
->GetValue());
3087 auto pS140
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject140
->Lookup("S"_ostr
));
3088 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pS140
->GetValue());
3091 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject140
->Lookup("K"_ostr
));
3094 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3096 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3097 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3106 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3107 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3108 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3109 pObjR
->LookupElement("Obj"_ostr
));
3110 auto pAnnot
= pAnnotRef
->LookupObject();
3112 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3113 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3115 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3116 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3117 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3118 pAnnot
->Lookup("Contents"_ostr
));
3119 CPPUNIT_ASSERT_EQUAL(
3120 u
"https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"_ustr
,
3121 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3122 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3124 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAnnot
->Lookup("A"_ostr
));
3125 CPPUNIT_ASSERT(pAA
);
3127 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAA
->LookupElement("Type"_ostr
));
3128 CPPUNIT_ASSERT_EQUAL("Action"_ostr
, pAAType
->GetValue());
3130 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAA
->LookupElement("S"_ostr
));
3131 CPPUNIT_ASSERT_EQUAL("URI"_ostr
, pAAS
->GetValue());
3132 auto pAAURI
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
3133 pAA
->LookupElement("URI"_ostr
));
3134 CPPUNIT_ASSERT_EQUAL(
3135 "https://vr-easy.com/tour/usr/220113-virtuellerschulausflug/#pano=24"_ostr
,
3136 pAAURI
->GetValue());
3139 CPPUNIT_ASSERT_EQUAL(1, nMCID
);
3140 CPPUNIT_ASSERT_EQUAL(1, nRef
);
3143 auto pRefKid16
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids1v
[6]);
3144 CPPUNIT_ASSERT(pRefKid16
);
3145 auto pObject16
= pRefKid16
->LookupObject();
3146 CPPUNIT_ASSERT(pObject16
);
3147 auto pType16
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject16
->Lookup("Type"_ostr
));
3148 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType16
->GetValue());
3149 auto pS16
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject16
->Lookup("S"_ostr
));
3150 CPPUNIT_ASSERT_EQUAL("Text#20body"_ostr
, pS16
->GetValue());
3152 auto pKids16
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject16
->Lookup("K"_ostr
));
3153 CPPUNIT_ASSERT(pKids16
);
3154 // assume there are no MCID ref at this level
3155 auto pKids16v
= pKids16
->GetElements();
3156 auto pRefKid160
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKids16v
[0]);
3157 CPPUNIT_ASSERT(pRefKid160
);
3158 auto pObject160
= pRefKid160
->LookupObject();
3159 CPPUNIT_ASSERT(pObject160
);
3160 auto pType160
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject160
->Lookup("Type"_ostr
));
3161 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType160
->GetValue());
3162 auto pS160
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject160
->Lookup("S"_ostr
));
3163 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pS160
->GetValue());
3164 auto pA160Dict
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pObject160
->Lookup("A"_ostr
));
3165 CPPUNIT_ASSERT(pA160Dict
);
3166 auto pA160O
= dynamic_cast<vcl::filter::PDFNameElement
*>(pA160Dict
->LookupElement("O"_ostr
));
3167 CPPUNIT_ASSERT(pA160O
);
3168 CPPUNIT_ASSERT_EQUAL("PrintField"_ostr
, pA160O
->GetValue());
3170 = dynamic_cast<vcl::filter::PDFNameElement
*>(pA160Dict
->LookupElement("Role"_ostr
));
3171 CPPUNIT_ASSERT(pA160Role
);
3172 CPPUNIT_ASSERT_EQUAL("tv"_ostr
, pA160Role
->GetValue());
3175 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject160
->Lookup("K"_ostr
));
3178 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3180 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3181 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3190 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3191 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3192 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3193 pObjR
->LookupElement("Obj"_ostr
));
3194 auto pAnnot
= pAnnotRef
->LookupObject();
3196 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3197 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3199 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3200 CPPUNIT_ASSERT_EQUAL("Widget"_ostr
, pASubtype
->GetValue());
3203 CPPUNIT_ASSERT_EQUAL(1, nMCID
);
3204 CPPUNIT_ASSERT_EQUAL(1, nRef
);
3208 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf135192
)
3211 uno::Sequence
<beans::PropertyValue
> aFilterData(
3212 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
3213 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
3215 vcl::filter::PDFDocument aDocument
;
3216 load(u
"tdf135192-1.fodp", aDocument
);
3218 // The document has one page.
3219 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
3220 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
3223 for (const auto& rDocElement
: aDocument
.GetElements())
3225 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
3228 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
3229 if (pType1
&& pType1
->GetValue() == "StructElem")
3231 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
3232 if (pS1
&& pS1
->GetValue() == "Table")
3236 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1
->Lookup("K"_ostr
));
3237 CPPUNIT_ASSERT(pKids1
);
3238 // there can be additional children, such as MCID ref
3239 for (auto pKid1
: pKids1
->GetElements())
3241 auto pRefKid1
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKid1
);
3244 auto pObject2
= pRefKid1
->LookupObject();
3247 auto pType2
= dynamic_cast<vcl::filter::PDFNameElement
*>(
3248 pObject2
->Lookup("Type"_ostr
));
3249 if (pType2
&& pType2
->GetValue() == "StructElem")
3251 auto pS2
= dynamic_cast<vcl::filter::PDFNameElement
*>(
3252 pObject2
->Lookup("S"_ostr
));
3253 if (pS2
&& pS2
->GetValue() == "TR")
3256 auto pKids2
= dynamic_cast<vcl::filter::PDFArrayElement
*>(
3257 pObject2
->Lookup("K"_ostr
));
3258 CPPUNIT_ASSERT(pKids2
);
3259 for (auto pKid2
: pKids2
->GetElements())
3262 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3266 auto pObject3
= pRefKid2
->LookupObject();
3270 = dynamic_cast<vcl::filter::PDFNameElement
*>(
3271 pObject3
->Lookup("Type"_ostr
));
3272 if (pType3
&& pType3
->GetValue() == "StructElem")
3274 auto pS3
= dynamic_cast<
3275 vcl::filter::PDFNameElement
*>(
3276 pObject3
->Lookup("S"_ostr
));
3277 if (nTR
== 0 && pS3
&& pS3
->GetValue() == "TH")
3280 auto pAttrs
= dynamic_cast<
3281 vcl::filter::PDFArrayElement
*>(
3282 pObject3
->Lookup("A"_ostr
));
3283 CPPUNIT_ASSERT(pAttrs
!= nullptr);
3284 for (const auto& rAttrRef
:
3285 pAttrs
->GetElements())
3287 auto pAttrDict
= dynamic_cast<
3288 vcl::filter::PDFDictionaryElement
*>(
3290 CPPUNIT_ASSERT(pAttrDict
!= nullptr);
3291 auto pOwner
= dynamic_cast<
3292 vcl::filter::PDFNameElement
*>(
3293 pAttrDict
->LookupElement("O"_ostr
));
3294 CPPUNIT_ASSERT(pOwner
!= nullptr);
3295 if (pOwner
->GetValue() == "Table")
3297 auto pScope
= dynamic_cast<
3298 vcl::filter::PDFNameElement
*>(
3299 pAttrDict
->LookupElement(
3301 CPPUNIT_ASSERT(pScope
!= nullptr);
3302 CPPUNIT_ASSERT_EQUAL(
3304 pScope
->GetValue());
3308 CPPUNIT_ASSERT_EQUAL(1, nOTable
);
3311 else if (nTR
!= 0 && pS3
3312 && pS3
->GetValue() == "TD")
3320 CPPUNIT_ASSERT_EQUAL(3, nTD
);
3327 CPPUNIT_ASSERT_EQUAL(2, nTR
);
3332 CPPUNIT_ASSERT_EQUAL(1, nTable
);
3335 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf154955
)
3338 uno::Sequence
<beans::PropertyValue
> aFilterData(
3339 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
3340 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
3341 vcl::filter::PDFDocument aDocument
;
3342 load(u
"grouped-shape.fodt", aDocument
);
3344 // The document has one page.
3345 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
3346 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
3348 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
3349 CPPUNIT_ASSERT(pContents
);
3350 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
3351 CPPUNIT_ASSERT(pStream
);
3352 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
3354 SvMemoryStream aUncompressed
;
3356 aZCodec
.BeginCompression();
3357 rObjectStream
.Seek(0);
3358 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
3359 CPPUNIT_ASSERT(aZCodec
.EndCompression());
3361 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
3362 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
3378 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
3383 std::string_view
const line(pStart
, pLine
- pStart
);
3385 if (!line
.empty() && line
[0] != '%')
3387 ::std::cerr
<< nLine
<< ": " << line
<< "\n";
3388 if (o3tl::starts_with(line
, "/Artifact "))
3390 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
3394 else if (o3tl::starts_with(line
, "/Figure<</MCID "))
3396 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
3400 else if (line
== "EMC")
3402 CPPUNIT_ASSERT_MESSAGE("unexpected end", state
!= Default
);
3405 else if (nLine
> 1) // first line is expected "0.1 w"
3407 CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state
!= Default
);
3411 CPPUNIT_ASSERT_EQUAL_MESSAGE("unclosed MCS", Default
, state
);
3412 CPPUNIT_ASSERT_EQUAL(2, nTagged
);
3413 CPPUNIT_ASSERT_GREATEREQUAL(1, nArtifacts
);
3416 for (const auto& rDocElement
: aDocument
.GetElements())
3418 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
3421 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
3422 if (pType
&& pType
->GetValue() == "StructElem")
3424 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
3425 if (pS
&& pS
->GetValue() == "Figure")
3430 CPPUNIT_ASSERT_EQUAL(u
"Two rectangles - Grouped"_ustr
,
3431 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
3432 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3433 pObject
->Lookup("Alt"_ostr
))));
3436 CPPUNIT_ASSERT_EQUAL(u
"these ones are green"_ustr
,
3437 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
3438 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3439 pObject
->Lookup("Alt"_ostr
))));
3444 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pObject
->Lookup("P"_ostr
));
3445 CPPUNIT_ASSERT(pParentRef
);
3446 auto pParent(pParentRef
->LookupObject());
3447 CPPUNIT_ASSERT(pParent
);
3449 = dynamic_cast<vcl::filter::PDFNameElement
*>(pParent
->Lookup("Type"_ostr
));
3450 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pParentType
->GetValue());
3452 = dynamic_cast<vcl::filter::PDFNameElement
*>(pParent
->Lookup("S"_ostr
));
3453 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pParentS
->GetValue());
3459 // the problem was that there were 4 shapes (the sub-shapes of the 2 groups)
3460 CPPUNIT_ASSERT_EQUAL(2, nFigure
);
3463 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf155190
)
3466 uno::Sequence
<beans::PropertyValue
> aFilterData(
3467 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
3468 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
3470 vcl::filter::PDFDocument aDocument
;
3471 load(u
"tdf155190.odt", aDocument
);
3473 // The document has one page.
3474 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
3475 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
3479 for (const auto& rDocElement
: aDocument
.GetElements())
3481 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
3484 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
3486 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
3487 // start with the text box
3488 if (pType1
&& pType1
->GetValue() == "StructElem" && pS1
&& pS1
->GetValue() == "Div")
3491 auto pKids1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject1
->Lookup("K"_ostr
));
3492 CPPUNIT_ASSERT(pKids1
);
3493 for (auto pKid1
: pKids1
->GetElements())
3495 auto pRefKid1
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKid1
);
3498 auto pObject2
= pRefKid1
->LookupObject();
3499 CPPUNIT_ASSERT(pObject2
);
3501 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject2
->Lookup("Type"_ostr
));
3502 CPPUNIT_ASSERT(pType2
);
3503 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType2
->GetValue());
3505 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject2
->Lookup("S"_ostr
));
3506 CPPUNIT_ASSERT_EQUAL("FigureCaption"_ostr
, pS2
->GetValue());
3508 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject2
->Lookup("K"_ostr
));
3509 CPPUNIT_ASSERT(pKids2
);
3510 // there are additional children, MCID ref
3511 for (auto pKid2
: pKids2
->GetElements())
3513 auto pRefKid2
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKid2
);
3516 auto pObject3
= pRefKid2
->LookupObject();
3517 CPPUNIT_ASSERT(pObject3
);
3518 auto pType3
= dynamic_cast<vcl::filter::PDFNameElement
*>(
3519 pObject3
->Lookup("Type"_ostr
));
3520 if (pType3
&& pType3
->GetValue() == "StructElem")
3522 auto pS3
= dynamic_cast<vcl::filter::PDFNameElement
*>(
3523 pObject3
->Lookup("S"_ostr
));
3524 CPPUNIT_ASSERT_EQUAL("Figure"_ostr
, pS3
->GetValue());
3525 auto pAlt
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3526 pObject3
->Lookup("Alt"_ostr
));
3527 CPPUNIT_ASSERT_EQUAL(
3528 u
"Picture of apples"_ustr
,
3529 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt
));
3530 auto pKids3
= dynamic_cast<vcl::filter::PDFArrayElement
*>(
3531 pObject3
->Lookup("K"_ostr
));
3532 CPPUNIT_ASSERT(pKids3
);
3533 // the problem was that this didn't reference an MCID
3534 CPPUNIT_ASSERT(!pKids3
->GetElements().empty());
3543 CPPUNIT_ASSERT_EQUAL(1, nDiv
);
3544 CPPUNIT_ASSERT_EQUAL(1, nFigure
);
3547 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testMediaShapeAnnot
)
3550 uno::Sequence
<beans::PropertyValue
> aFilterData(
3551 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
3552 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
3554 vcl::filter::PDFDocument aDocument
;
3555 load(u
"vid.odt", aDocument
);
3557 // The document has one page.
3558 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
3559 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
3561 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
3562 CPPUNIT_ASSERT(pAnnots
);
3564 // There should be one annotation
3565 CPPUNIT_ASSERT_EQUAL(size_t(1), pAnnots
->GetElements().size());
3566 auto pAnnotReference
3567 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pAnnots
->GetElements()[0]);
3568 CPPUNIT_ASSERT(pAnnotReference
);
3569 // check /Annot - produced by sw
3570 vcl::filter::PDFObjectElement
* pAnnot
= pAnnotReference
->LookupObject();
3571 CPPUNIT_ASSERT(pAnnot
);
3572 CPPUNIT_ASSERT_EQUAL(
3574 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
))->GetValue());
3575 CPPUNIT_ASSERT_EQUAL(
3577 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
))->GetValue());
3579 auto pA
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAnnot
->Lookup("A"_ostr
));
3581 auto pR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pA
->LookupElement("R"_ostr
));
3583 auto pC
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pR
->LookupElement("C"_ostr
));
3585 auto pCT
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(pC
->LookupElement("CT"_ostr
));
3586 CPPUNIT_ASSERT_EQUAL("video/webm"_ostr
, pCT
->GetValue());
3587 auto pD
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pC
->LookupElement("D"_ostr
));
3589 auto pDesc
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pD
->LookupElement("Desc"_ostr
));
3590 CPPUNIT_ASSERT(pDesc
);
3591 CPPUNIT_ASSERT_EQUAL(u
"alternativloser text\nand some description"_ustr
,
3592 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pDesc
));
3593 auto pAlt
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pC
->LookupElement("Alt"_ostr
));
3594 CPPUNIT_ASSERT(pAlt
);
3595 auto pLang
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(pAlt
->GetElement(0));
3596 CPPUNIT_ASSERT_EQUAL(""_ostr
, pLang
->GetValue());
3597 auto pAltText
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pAlt
->GetElement(1));
3598 CPPUNIT_ASSERT_EQUAL(u
"alternativloser text\nand some description"_ustr
,
3599 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAltText
));
3602 = dynamic_cast<vcl::filter::PDFNumberElement
*>(pAnnot
->Lookup("StructParent"_ostr
));
3603 CPPUNIT_ASSERT(pStructParent
);
3605 vcl::filter::PDFReferenceElement
* pStructElemRef(nullptr);
3607 // check ParentTree to find StructElem
3609 for (const auto& rDocElement
: aDocument
.GetElements())
3611 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
3614 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
3615 if (pType1
&& pType1
->GetValue() == "StructTreeRoot")
3618 auto pParentTree
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3619 pObject1
->Lookup("ParentTree"_ostr
));
3620 CPPUNIT_ASSERT(pParentTree
);
3621 auto pNumTree
= pParentTree
->LookupObject();
3622 CPPUNIT_ASSERT(pNumTree
);
3623 auto pNums
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pNumTree
->Lookup("Nums"_ostr
));
3624 CPPUNIT_ASSERT(pNums
);
3626 for (size_t i
= 0; i
< pNums
->GetElements().size(); i
+= 2)
3628 auto pI
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pNums
->GetElement(i
));
3629 if (pI
->GetValue() == pStructParent
->GetValue())
3632 CPPUNIT_ASSERT(i
< pNums
->GetElements().size() - 1);
3634 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pNums
->GetElement(i
+ 1));
3635 CPPUNIT_ASSERT(pStructElemRef
);
3638 CPPUNIT_ASSERT_EQUAL(1, nFound
);
3641 CPPUNIT_ASSERT_EQUAL(1, nRoots
);
3643 // check /StructElem - produced by drawinglayer
3644 CPPUNIT_ASSERT(pStructElemRef
);
3645 auto pStructElem(pStructElemRef
->LookupObject());
3646 CPPUNIT_ASSERT(pStructElem
);
3648 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pStructElem
->Lookup("Type"_ostr
));
3649 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType
->GetValue());
3650 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pStructElem
->Lookup("S"_ostr
));
3651 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pS
->GetValue());
3652 auto pSEAlt
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pStructElem
->Lookup("Alt"_ostr
));
3653 CPPUNIT_ASSERT_EQUAL(u
"alternativloser text - and some description"_ustr
,
3654 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pSEAlt
));
3655 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pStructElem
->Lookup("K"_ostr
));
3658 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3660 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3661 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3670 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3671 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3673 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pObjR
->LookupElement("Obj"_ostr
));
3674 CPPUNIT_ASSERT_EQUAL(pAnnot
, pAnnotRef
->LookupObject());
3677 CPPUNIT_ASSERT_EQUAL(1, nMCID
);
3678 CPPUNIT_ASSERT_EQUAL(1, nRef
);
3681 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testFlyFrameHyperlinkAnnot
)
3684 uno::Sequence
<beans::PropertyValue
> aFilterData(
3685 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
3686 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
3688 vcl::filter::PDFDocument aDocument
;
3689 load(u
"image-hyperlink-alttext.fodt", aDocument
);
3691 // The document has one page.
3692 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
3693 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
3695 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
3696 CPPUNIT_ASSERT(pAnnots
);
3698 // There should be one annotation
3699 CPPUNIT_ASSERT_EQUAL(size_t(1), pAnnots
->GetElements().size());
3700 auto pAnnotReference
3701 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pAnnots
->GetElements()[0]);
3702 CPPUNIT_ASSERT(pAnnotReference
);
3703 // check /Annot - produced by sw
3704 vcl::filter::PDFObjectElement
* pAnnot
= pAnnotReference
->LookupObject();
3705 CPPUNIT_ASSERT(pAnnot
);
3706 CPPUNIT_ASSERT_EQUAL(
3708 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
))->GetValue());
3709 CPPUNIT_ASSERT_EQUAL(
3711 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
))->GetValue());
3714 = dynamic_cast<vcl::filter::PDFHexStringElement
*>(pAnnot
->Lookup("Contents"_ostr
));
3715 CPPUNIT_ASSERT_EQUAL(u
"Image2"_ustr
,
3716 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pContents
));
3719 = dynamic_cast<vcl::filter::PDFNumberElement
*>(pAnnot
->Lookup("StructParent"_ostr
));
3720 CPPUNIT_ASSERT(pStructParent
);
3722 vcl::filter::PDFReferenceElement
* pStructElemRef(nullptr);
3724 // check ParentTree to find StructElem
3726 for (const auto& rDocElement
: aDocument
.GetElements())
3728 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
3731 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
3732 if (pType1
&& pType1
->GetValue() == "StructTreeRoot")
3735 auto pParentTree
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3736 pObject1
->Lookup("ParentTree"_ostr
));
3737 CPPUNIT_ASSERT(pParentTree
);
3738 auto pNumTree
= pParentTree
->LookupObject();
3739 CPPUNIT_ASSERT(pNumTree
);
3740 auto pNums
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pNumTree
->Lookup("Nums"_ostr
));
3741 CPPUNIT_ASSERT(pNums
);
3743 for (size_t i
= 0; i
< pNums
->GetElements().size(); i
+= 2)
3745 auto pI
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pNums
->GetElement(i
));
3746 if (pI
->GetValue() == pStructParent
->GetValue())
3749 CPPUNIT_ASSERT(i
< pNums
->GetElements().size() - 1);
3751 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pNums
->GetElement(i
+ 1));
3752 CPPUNIT_ASSERT(pStructElemRef
);
3755 CPPUNIT_ASSERT_EQUAL(1, nFound
);
3758 CPPUNIT_ASSERT_EQUAL(1, nRoots
);
3760 // check /StructElem - produced by sw painting code
3761 CPPUNIT_ASSERT(pStructElemRef
);
3762 auto pStructElem(pStructElemRef
->LookupObject());
3763 CPPUNIT_ASSERT(pStructElem
);
3765 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pStructElem
->Lookup("Type"_ostr
));
3766 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType
->GetValue());
3767 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pStructElem
->Lookup("S"_ostr
));
3768 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pS
->GetValue());
3769 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pStructElem
->Lookup("K"_ostr
));
3772 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3774 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3775 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3784 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3785 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3787 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pObjR
->LookupElement("Obj"_ostr
));
3788 CPPUNIT_ASSERT_EQUAL(pAnnot
, pAnnotRef
->LookupObject());
3791 CPPUNIT_ASSERT_EQUAL(1, nMCID
);
3792 CPPUNIT_ASSERT_EQUAL(1, nRef
);
3794 // the Link is inside a Figure
3796 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pStructElem
->Lookup("P"_ostr
));
3797 CPPUNIT_ASSERT(pParentRef
);
3798 auto pParent(pParentRef
->LookupObject());
3799 CPPUNIT_ASSERT(pParent
);
3800 auto pParentType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pParent
->Lookup("Type"_ostr
));
3801 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pParentType
->GetValue());
3802 auto pParentS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pParent
->Lookup("S"_ostr
));
3803 CPPUNIT_ASSERT_EQUAL("Figure"_ostr
, pParentS
->GetValue());
3804 auto pAlt
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pParent
->Lookup("Alt"_ostr
));
3805 CPPUNIT_ASSERT_EQUAL(u
"Ship drawing - Very cute"_ustr
,
3806 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt
));
3809 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testFormControlAnnot
)
3812 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
3813 { "PDFUACompliance", uno::Any(true) },
3814 { "ExportFormFields", uno::Any(true) },
3816 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
3818 vcl::filter::PDFDocument aDocument
;
3819 load(u
"formcontrol.fodt", aDocument
);
3821 // The document has one page.
3822 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
3823 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
3825 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
3826 CPPUNIT_ASSERT(pAnnots
);
3828 // There should be one annotation
3829 CPPUNIT_ASSERT_EQUAL(size_t(1), pAnnots
->GetElements().size());
3830 auto pAnnotReference
3831 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pAnnots
->GetElements()[0]);
3832 CPPUNIT_ASSERT(pAnnotReference
);
3834 vcl::filter::PDFObjectElement
* pAnnot
= pAnnotReference
->LookupObject();
3835 CPPUNIT_ASSERT(pAnnot
);
3836 CPPUNIT_ASSERT_EQUAL(
3838 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
))->GetValue());
3839 CPPUNIT_ASSERT_EQUAL(
3841 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
))->GetValue());
3842 auto pT
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(pAnnot
->Lookup("T"_ostr
));
3844 CPPUNIT_ASSERT_EQUAL("Check Box 1"_ostr
, pT
->GetValue());
3845 auto pTU
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pAnnot
->Lookup("TU"_ostr
));
3846 CPPUNIT_ASSERT(pTU
);
3847 CPPUNIT_ASSERT_EQUAL(u
"helpful text"_ustr
,
3848 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pTU
));
3851 = dynamic_cast<vcl::filter::PDFNumberElement
*>(pAnnot
->Lookup("StructParent"_ostr
));
3852 CPPUNIT_ASSERT(pStructParent
);
3854 vcl::filter::PDFReferenceElement
* pStructElemRef(nullptr);
3856 // check ParentTree to find StructElem
3858 for (const auto& rDocElement
: aDocument
.GetElements())
3860 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
3863 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
3864 if (pType1
&& pType1
->GetValue() == "StructTreeRoot")
3867 auto pParentTree
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3868 pObject1
->Lookup("ParentTree"_ostr
));
3869 CPPUNIT_ASSERT(pParentTree
);
3870 auto pNumTree
= pParentTree
->LookupObject();
3871 CPPUNIT_ASSERT(pNumTree
);
3872 auto pNums
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pNumTree
->Lookup("Nums"_ostr
));
3873 CPPUNIT_ASSERT(pNums
);
3875 for (size_t i
= 0; i
< pNums
->GetElements().size(); i
+= 2)
3877 auto pI
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pNums
->GetElement(i
));
3878 if (pI
->GetValue() == pStructParent
->GetValue())
3881 CPPUNIT_ASSERT(i
< pNums
->GetElements().size() - 1);
3883 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pNums
->GetElement(i
+ 1));
3884 CPPUNIT_ASSERT(pStructElemRef
);
3887 CPPUNIT_ASSERT_EQUAL(1, nFound
);
3890 CPPUNIT_ASSERT_EQUAL(1, nRoots
);
3892 // check /StructElem
3893 CPPUNIT_ASSERT(pStructElemRef
);
3894 auto pStructElem(pStructElemRef
->LookupObject());
3895 CPPUNIT_ASSERT(pStructElem
);
3897 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pStructElem
->Lookup("Type"_ostr
));
3898 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pType
->GetValue());
3899 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pStructElem
->Lookup("S"_ostr
));
3900 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pS
->GetValue());
3901 auto pAlt
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pStructElem
->Lookup("Alt"_ostr
));
3902 CPPUNIT_ASSERT_EQUAL(u
"textuelle alternative - a box to check"_ustr
,
3903 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAlt
));
3904 auto pA
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pStructElem
->Lookup("A"_ostr
));
3906 auto pO
= dynamic_cast<vcl::filter::PDFNameElement
*>(pA
->LookupElement("O"_ostr
));
3908 CPPUNIT_ASSERT_EQUAL("PrintField"_ostr
, pO
->GetValue());
3909 auto pRole
= dynamic_cast<vcl::filter::PDFNameElement
*>(pA
->LookupElement("Role"_ostr
));
3910 CPPUNIT_ASSERT(pRole
);
3911 CPPUNIT_ASSERT_EQUAL("cb"_ostr
, pRole
->GetValue());
3912 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pStructElem
->Lookup("K"_ostr
));
3915 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3917 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3918 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3927 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3928 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3930 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pObjR
->LookupElement("Obj"_ostr
));
3931 CPPUNIT_ASSERT_EQUAL(pAnnot
, pAnnotRef
->LookupObject());
3934 CPPUNIT_ASSERT_EQUAL(1, nMCID
);
3935 CPPUNIT_ASSERT_EQUAL(1, nRef
);
3938 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf142129
)
3940 loadFromFile(u
"master.odm");
3942 // update linked section
3943 dispatchCommand(mxComponent
, u
".uno:UpdateAllLinks"_ustr
, {});
3945 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
3947 // Enable Outlines export
3948 uno::Sequence
<beans::PropertyValue
> aFilterData(
3949 comphelper::InitPropertySequence({ { "ExportBookmarks", uno::Any(true) } }));
3950 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
3951 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
3953 // Parse the export result.
3954 vcl::filter::PDFDocument aDocument
;
3955 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
3956 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
3958 auto* pCatalog
= aDocument
.GetCatalog();
3959 CPPUNIT_ASSERT(pCatalog
);
3960 auto* pCatalogDictionary
= pCatalog
->GetDictionary();
3961 CPPUNIT_ASSERT(pCatalogDictionary
);
3962 auto* pOutlinesObject
= pCatalogDictionary
->LookupObject("Outlines"_ostr
);
3963 CPPUNIT_ASSERT(pOutlinesObject
);
3964 auto* pOutlinesDictionary
= pOutlinesObject
->GetDictionary();
3966 // Type isn't actually written currently
3968 = dynamic_cast<vcl::filter::PDFNameElement
*>(pOutlinesDictionary
->LookupElement("Type"));
3969 CPPUNIT_ASSERT(pType
);
3970 CPPUNIT_ASSERT_EQUAL(OString("Outlines"), pType
->GetValue());
3973 auto* pFirst
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3974 pOutlinesDictionary
->LookupElement("First"_ostr
));
3975 CPPUNIT_ASSERT(pFirst
);
3976 auto* pFirstD
= pFirst
->LookupObject()->GetDictionary();
3977 CPPUNIT_ASSERT(pFirstD
);
3978 //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirstD->LookupElement("Type"))->GetValue());
3979 CPPUNIT_ASSERT_EQUAL(u
"Preface"_ustr
, ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
3980 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3981 pFirstD
->LookupElement("Title"_ostr
))));
3984 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pFirstD
->LookupElement("First"_ostr
));
3985 CPPUNIT_ASSERT(pFirst1
);
3986 auto* pFirst1D
= pFirst1
->LookupObject()->GetDictionary();
3987 CPPUNIT_ASSERT(pFirst1D
);
3988 // here is a hidden section with headings "Copyright" etc.; check that
3989 // there are no outline entries for it
3990 //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst1D->LookupElement("Type"))->GetValue());
3991 CPPUNIT_ASSERT_EQUAL(u
"Who is this book for?"_ustr
,
3992 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
3993 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3994 pFirst1D
->LookupElement("Title"_ostr
))));
3997 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pFirst1D
->LookupElement("Next"_ostr
));
3998 auto* pFirst2D
= pFirst2
->LookupObject()->GetDictionary();
3999 CPPUNIT_ASSERT(pFirst2D
);
4000 //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst2D->LookupElement("Type"))->GetValue());
4001 CPPUNIT_ASSERT_EQUAL(u
"What\u2019s in this book?"_ustr
,
4002 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
4003 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
4004 pFirst2D
->LookupElement("Title"_ostr
))));
4007 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pFirst2D
->LookupElement("Next"_ostr
));
4008 auto* pFirst3D
= pFirst3
->LookupObject()->GetDictionary();
4009 CPPUNIT_ASSERT(pFirst3D
);
4010 //CPPUNIT_ASSERT_EQUAL(OString("Outlines"), dynamic_cast<vcl::filter::PDFNameElement*>(pFirst3D->LookupElement("Type"))->GetValue());
4011 CPPUNIT_ASSERT_EQUAL(u
"Minimum requirements for using LibreOffice"_ustr
,
4012 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
4013 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
4014 pFirst3D
->LookupElement("Title"_ostr
))));
4016 CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement
*>(nullptr),
4017 pFirst3D
->LookupElement("Next"_ostr
));
4018 CPPUNIT_ASSERT_EQUAL(static_cast<vcl::filter::PDFElement
*>(nullptr),
4019 pFirstD
->LookupElement("Next"_ostr
));
4022 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPdfImageRotate180
)
4024 // Create an empty document.
4025 loadFromURL(u
"private:factory/swriter"_ustr
);
4026 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
4027 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
4028 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
4030 // Insert the PDF image.
4031 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
4032 uno::Reference
<beans::XPropertySet
> xGraphicObject(
4033 xFactory
->createInstance(u
"com.sun.star.text.TextGraphicObject"_ustr
), uno::UNO_QUERY
);
4034 OUString aURL
= createFileURL(u
"pdf-image-rotate-180.pdf");
4035 xGraphicObject
->setPropertyValue(u
"GraphicURL"_ustr
, uno::Any(aURL
));
4036 uno::Reference
<drawing::XShape
> xShape(xGraphicObject
, uno::UNO_QUERY
);
4037 xShape
->setSize(awt::Size(1000, 1000));
4038 uno::Reference
<text::XTextContent
> xTextContent(xGraphicObject
, uno::UNO_QUERY
);
4039 xText
->insertTextContent(xCursor
->getStart(), xTextContent
, /*bAbsorb=*/false);
4042 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
4043 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
4045 // Parse the export result.
4046 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4047 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4049 // Make sure that the page -> form -> form has a child image.
4050 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4051 CPPUNIT_ASSERT(pPdfPage
);
4052 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getObjectCount());
4053 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(0);
4054 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form
, pPageObject
->getType());
4055 // 2: white background and the actual object.
4056 CPPUNIT_ASSERT_EQUAL(2, pPageObject
->getFormObjectCount());
4057 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pFormObject
= pPageObject
->getFormObject(1);
4058 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form
, pFormObject
->getType());
4059 CPPUNIT_ASSERT_EQUAL(1, pFormObject
->getFormObjectCount());
4061 // Check if the inner form object (original page object in the pdf image) has the correct
4063 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pInnerFormObject
= pFormObject
->getFormObject(0);
4064 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form
, pInnerFormObject
->getType());
4065 CPPUNIT_ASSERT_EQUAL(1, pInnerFormObject
->getFormObjectCount());
4066 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pImage
= pInnerFormObject
->getFormObject(0);
4067 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Image
, pImage
->getType());
4068 basegfx::B2DHomMatrix aMat
= pInnerFormObject
->getMatrix();
4069 basegfx::B2DTuple aScale
;
4070 basegfx::B2DTuple aTranslate
;
4073 aMat
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
4074 // Without the accompanying fix in place, this test would have failed with:
4077 // i.e. the 180 degrees rotation didn't happen (via a combination of horizontal + vertical
4079 CPPUNIT_ASSERT_DOUBLES_EQUAL(-1.0, aScale
.getX(), 0.01);
4082 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf144222
)
4084 // Assume Windows has the font for U+4E2D
4086 saveAsPDF(u
"tdf144222.ods");
4087 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4089 // The document has one page.
4090 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4091 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4092 CPPUNIT_ASSERT(pPdfPage
);
4093 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
4094 CPPUNIT_ASSERT(pTextPage
);
4096 int nPageObjectCount
= pPdfPage
->getObjectCount();
4097 const OUString sChar
= u
"\u4E2D"_ustr
;
4098 basegfx::B2DRectangle aRect1
, aRect2
;
4101 for (int i
= 0; i
< nPageObjectCount
; ++i
)
4103 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPdfPageObject
= pPdfPage
->getObject(i
);
4104 if (pPdfPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
4107 OUString sText
= pPdfPageObject
->getText(pTextPage
);
4109 aRect1
= pPdfPageObject
->getBounds();
4111 aRect2
= pPdfPageObject
->getBounds();
4115 CPPUNIT_ASSERT_EQUAL(2, nCount
);
4116 CPPUNIT_ASSERT(!aRect1
.isEmpty());
4117 CPPUNIT_ASSERT(!aRect2
.isEmpty());
4118 CPPUNIT_ASSERT(!aRect1
.overlaps(aRect2
));
4122 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf145873
)
4124 // Import the bugdoc and export as PDF.
4125 saveAsPDF(u
"tdf145873.pptx");
4127 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4129 // The document has one page.
4130 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4132 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4133 CPPUNIT_ASSERT(pPdfPage
);
4134 int nPageObjectCount
= pPdfPage
->getObjectCount();
4136 // tdf#145873: Without the fix #1 in place, this test would have failed with
4139 CPPUNIT_ASSERT_EQUAL(66, nPageObjectCount
);
4141 auto pObject
= pPdfPage
->getObject(4);
4142 CPPUNIT_ASSERT_MESSAGE("no object", pObject
!= nullptr);
4144 // tdf#145873: Without the fix #2 in place, this test would have failed with
4145 // - Expected: 13.40
4146 // - Actual : 3.57...
4148 CPPUNIT_ASSERT_DOUBLES_EQUAL(13.40, pObject
->getBounds().getWidth(), 0.1);
4149 // - Expected: 13.79
4150 // - Actual : 3.74...
4152 CPPUNIT_ASSERT_DOUBLES_EQUAL(13.79, pObject
->getBounds().getHeight(), 0.1);
4155 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPdfImageHyperlink
)
4157 // Given a Draw file, containing a PDF image, which has a hyperlink in it:
4158 // When saving to PDF:
4159 saveAsPDF(u
"pdf-image-hyperlink.odg");
4161 // Then make sure that link is preserved:
4162 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4163 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4164 CPPUNIT_ASSERT(pPdfPage
);
4165 // Without the accompanying fix in place, this test would have failed, the hyperlink of the PDF
4167 CPPUNIT_ASSERT(pPdfPage
->hasLinks());
4169 // Also test the precision of the form XObject.
4170 // Given a full-page form XObject, page height is 27.94 cm (792 points):
4171 // When writing the reciprocal of the object height to PDF:
4172 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pFormObject
;
4173 for (int i
= 0; i
< pPdfPage
->getObjectCount(); ++i
)
4175 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pObject
= pPdfPage
->getObject(i
);
4176 if (pObject
->getType() == vcl::pdf::PDFPageObjectType::Form
)
4178 pFormObject
= std::move(pObject
);
4182 CPPUNIT_ASSERT(pFormObject
);
4183 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pInnerFormObject
;
4184 for (int i
= 0; i
< pFormObject
->getFormObjectCount(); ++i
)
4186 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pObject
= pFormObject
->getFormObject(i
);
4187 if (pObject
->getType() == vcl::pdf::PDFPageObjectType::Form
)
4189 pInnerFormObject
= std::move(pObject
);
4193 CPPUNIT_ASSERT(pInnerFormObject
);
4194 // Then make sure that enough digits are used, so the point size is unchanged:
4195 basegfx::B2DHomMatrix aMatrix
= pInnerFormObject
->getMatrix();
4196 basegfx::B2DTuple aScale
;
4197 basegfx::B2DTuple aTranslate
;
4200 aMatrix
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
4201 // Without the accompanying fix in place, this test would have failed with:
4202 // - Expected: 0.0012626264
4203 // - Actual : 0.00126
4204 // i.e. the rounded reciprocal was 794 points, not the original 792.
4205 CPPUNIT_ASSERT_EQUAL(0.001262626, rtl::math::round(aScale
.getY(), 9));
4208 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testURIs
)
4216 u
"http://example.com/"_ustr
,
4217 "http://example.com/"_ostr
,
4221 u
"file://localfile.odt/"_ustr
,
4222 "file://localfile.odt/"_ostr
,
4227 u
"http://username:password@example.com"_ustr
,
4228 "http://username:password@example.com"_ostr
,
4232 u
"git://git.example.org/project/example"_ustr
,
4233 "git://git.example.org/project/example"_ostr
,
4237 // The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
4238 u
"filebypath.odt"_ustr
,
4239 "filebypath.pdf"_ostr
,
4243 // The odt/pdf gets substituted due to 'ConvertOOoTargetToPDFTarget'
4244 // but this time with ExportLinksRelativeFsys off the path is added
4245 u
"filebypath.odt"_ustr
,
4246 OUStringToOString(utl::GetTempNameBaseDirectory(), RTL_TEXTENCODING_UTF8
)
4251 // This also gets made relative due to 'ExportLinksRelativeFsys'
4252 utl::GetTempNameBaseDirectory() + "fileintempdir.odt",
4253 "fileintempdir.pdf"_ostr
,
4257 // Create an empty document.
4258 // Note: The test harness gets very upset if we try and create multiple
4259 // documents, or recreate it; so reuse one instance for all the links
4260 loadFromURL(u
"private:factory/swriter"_ustr
);
4261 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
4262 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
4263 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
4264 xText
->insertString(xCursor
, u
"Test pdf"_ustr
, /*bAbsorb=*/false);
4266 // Set the name so it can do relative name replacement
4267 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
4268 xModel
->attachResource(maTempFile
.GetURL(), xModel
->getArgs());
4270 for (unsigned int i
= 0; i
< (sizeof(URIs
) / sizeof(URIs
[0])); i
++)
4272 // Test the filename rewriting
4273 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
4274 { "ExportLinksRelativeFsys", uno::Any(URIs
[i
].relativeFsys
) },
4275 { "ConvertOOoTargetToPDFTarget", uno::Any(true) },
4277 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
4279 // Add a link (based on testNestedHyperlink in rtfexport3)
4280 xCursor
->gotoStart(/*bExpand=*/false);
4281 xCursor
->gotoEnd(/*bExpand=*/true);
4282 uno::Reference
<beans::XPropertySet
> xCursorProps(xCursor
, uno::UNO_QUERY
);
4283 xCursorProps
->setPropertyValue(u
"HyperLinkURL"_ustr
, uno::Any(URIs
[i
].in
));
4286 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
4287 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
4289 // Use the filter rather than the pdfium route, as per the tdf105093 test, it's
4290 // easier to parse the annotations
4291 vcl::filter::PDFDocument aDocument
;
4293 // Parse the export result.
4294 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
4295 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
4297 // The document has one page.
4298 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
4299 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
4301 = dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
4302 CPPUNIT_ASSERT(pAnnots
);
4304 // There should be one annotation
4305 CPPUNIT_ASSERT_EQUAL(size_t(1), pAnnots
->GetElements().size());
4306 auto pAnnotReference
4307 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pAnnots
->GetElements()[0]);
4308 CPPUNIT_ASSERT(pAnnotReference
);
4309 vcl::filter::PDFObjectElement
* pAnnot
= pAnnotReference
->LookupObject();
4310 CPPUNIT_ASSERT(pAnnot
);
4311 // We're expecting something like /Type /Annot /A << /Type /Action /S /URI /URI (path)
4312 CPPUNIT_ASSERT_EQUAL(
4314 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
))->GetValue());
4315 CPPUNIT_ASSERT_EQUAL(
4317 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
))->GetValue());
4318 auto pAction
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAnnot
->Lookup("A"_ostr
));
4319 CPPUNIT_ASSERT(pAction
);
4320 auto pURIElem
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
4321 pAction
->LookupElement("URI"_ostr
));
4322 CPPUNIT_ASSERT(pURIElem
);
4324 CPPUNIT_ASSERT_EQUAL(URIs
[i
].out
, pURIElem
->GetValue());
4325 // tdf#148934 check a11y
4326 CPPUNIT_ASSERT_EQUAL(u
"Test pdf"_ustr
, ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
4327 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
4328 pAnnot
->Lookup("Contents"_ostr
))));
4332 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPdfImageAnnots
)
4334 // Given a document with a PDF image that has 2 comments (popup, text) and a hyperlink:
4335 // When saving to PDF:
4336 saveAsPDF(u
"pdf-image-annots.odg");
4338 // Then make sure only the hyperlink is kept, since Draw itself has its own comments:
4339 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4340 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4341 CPPUNIT_ASSERT(pPdfPage
);
4342 // Without the accompanying fix in place, this test would have failed with:
4345 // i.e. not only the hyperlink but also the 2 comments were exported, leading to duplication.
4346 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getAnnotationCount());
4349 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPdfImageEncryption
)
4351 // Given an empty document, with an inserted PDF image:
4352 loadFromURL(u
"private:factory/swriter"_ustr
);
4353 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
4354 uno::Reference
<text::XText
> xText
= xTextDocument
->getText();
4355 uno::Reference
<text::XTextCursor
> xCursor
= xText
->createTextCursor();
4356 uno::Reference
<lang::XMultiServiceFactory
> xFactory(mxComponent
, uno::UNO_QUERY
);
4357 uno::Reference
<beans::XPropertySet
> xGraphicObject(
4358 xFactory
->createInstance(u
"com.sun.star.text.TextGraphicObject"_ustr
), uno::UNO_QUERY
);
4359 OUString aURL
= createFileURL(u
"rectangles.pdf");
4360 xGraphicObject
->setPropertyValue(u
"GraphicURL"_ustr
, uno::Any(aURL
));
4361 uno::Reference
<drawing::XShape
> xShape(xGraphicObject
, uno::UNO_QUERY
);
4362 xShape
->setSize(awt::Size(1000, 1000));
4363 uno::Reference
<text::XTextContent
> xTextContent(xGraphicObject
, uno::UNO_QUERY
);
4364 xText
->insertTextContent(xCursor
->getStart(), xTextContent
, /*bAbsorb=*/false);
4366 // When saving as encrypted PDF:
4367 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
4368 uno::Sequence
<beans::PropertyValue
> aFilterData
= {
4369 comphelper::makePropertyValue(u
"EncryptFile"_ustr
, true),
4370 comphelper::makePropertyValue(u
"DocumentOpenPassword"_ustr
, u
"secret"_ustr
),
4372 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
4373 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
4375 // Then make sure that the image is not lost:
4376 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport("secret"_ostr
);
4377 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4378 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4379 CPPUNIT_ASSERT(pPdfPage
);
4380 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getObjectCount());
4381 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(0);
4382 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Form
, pPageObject
->getType());
4383 // Without the accompanying fix in place, this test would have failed with:
4386 // i.e. instead of the white background and the actual form child, the image was lost due to
4387 // missing encryption.
4388 CPPUNIT_ASSERT_EQUAL(2, pPageObject
->getFormObjectCount());
4391 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testBitmapScaledown
)
4393 // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
4394 // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
4395 if (!IsDefaultDPI())
4398 // Given a document with an upscaled and rotated barcode bitmap in it:
4399 // When saving as PDF:
4400 saveAsPDF(u
"bitmap-scaledown.odt");
4402 // Then verify that the bitmap is not downscaled:
4403 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4404 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4405 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4406 CPPUNIT_ASSERT(pPdfPage
);
4407 int nPageObjectCount
= pPdfPage
->getObjectCount();
4408 for (int i
= 0; i
< nPageObjectCount
; ++i
)
4410 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
4411 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Image
)
4414 std::unique_ptr
<vcl::pdf::PDFiumBitmap
> pBitmap
= pPageObject
->getImageBitmap();
4415 CPPUNIT_ASSERT(pBitmap
);
4416 // In-file sizes: good is 2631x380, bad is 1565x14.
4417 int nWidth
= pBitmap
->getWidth();
4418 // Without the accompanying fix in place, this test would have failed with:
4421 // i.e. the bitmap in the pdf result was small enough to be blurry.
4422 CPPUNIT_ASSERT_EQUAL(2616, nWidth
);
4426 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf139627
)
4429 saveAsPDF(u
"justified-arabic-kashida.odt");
4430 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4432 // The document has one page.
4433 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4434 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4435 CPPUNIT_ASSERT(pPdfPage
);
4437 // 7 objects, 3 text, others are path
4438 int nPageObjectCount
= pPdfPage
->getObjectCount();
4439 CPPUNIT_ASSERT_EQUAL(7, nPageObjectCount
);
4444 /* With "Noto Sans Arabic" font, these are the X ranges on Linux:
4445 0: ( 61.75 - 415.94)
4446 1: (479.70 - 422.40)
4449 basegfx::B2DRectangle aRect
[3];
4451 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
4452 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
;
4454 int nTextObjectCount
= 0;
4455 for (int i
= 0; i
< nPageObjectCount
; ++i
)
4457 pPageObject
= pPdfPage
->getObject(i
);
4458 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
4459 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
4461 sText
[nTextObjectCount
] = pPageObject
->getText(pTextPage
);
4462 aRect
[nTextObjectCount
] = pPageObject
->getBounds();
4466 CPPUNIT_ASSERT_EQUAL(3, nTextObjectCount
);
4468 // Text: جِـرم (which means "mass" in Persian)
4469 // Rendered as (left to right): "reh + mim" - "kasreh" - "jeh + tatweel"
4470 int rehmim
= 0, kasreh
= 1, jehtatweel
= 2;
4472 CPPUNIT_ASSERT_EQUAL(u
"رم"_ustr
, sText
[rehmim
].trim());
4473 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, sText
[kasreh
].trim());
4474 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, sText
[jehtatweel
].trim());
4476 // "Kasreh" should be within "jeh" character
4477 CPPUNIT_ASSERT_GREATER(aRect
[jehtatweel
].getMinX(), aRect
[kasreh
].getMinX());
4478 CPPUNIT_ASSERT_LESS(aRect
[jehtatweel
].getMaxX(), aRect
[kasreh
].getMaxX());
4480 // "Tatweel" should cover "jeh" and "reh"+"mim" to avoid gap
4481 // Checking right gap
4482 //CPPUNIT_ASSERT_GREATER(aRect[jehtatweel].getMinX(), aRect[tatweel].getMaxX());
4483 // Checking left gap
4484 // Kashida fails to reach to rehmim before the series of patches starting
4485 // with 3901e029bd39575f700e69a73818565d62226a23. The visible symptom is
4486 // a gap in the left of Kashida.
4487 CPPUNIT_ASSERT_LESS(aRect
[rehmim
].getMaxX(), aRect
[jehtatweel
].getMinX());
4489 // Overlappings of Kashida and surrounding characters is ~9% of the width
4490 // of the "jeh" character, while using "Noto Arabic Sans" font in this
4491 // specific example.
4492 // We set the hard limit of 10% here.
4493 CPPUNIT_ASSERT_LESS(0.1, fabs(aRect
[rehmim
].getMaxX() - aRect
[jehtatweel
].getMinX())
4494 / aRect
[jehtatweel
].getWidth());
4498 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testRexportRefToKids
)
4500 // We need to enable PDFium import (and make sure to disable after the test)
4501 bool bResetEnvVar
= false;
4502 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
4504 bResetEnvVar
= true;
4505 osl_setEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
, u
"1"_ustr
.pData
);
4507 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
4509 osl_clearEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
);
4512 // Load the PDF and save as PDF
4513 vcl::filter::PDFDocument aDocument
;
4514 load(u
"ref-to-kids.pdf", aDocument
);
4516 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
4517 CPPUNIT_ASSERT_EQUAL(size_t(5), aPages
.size());
4519 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
4520 CPPUNIT_ASSERT(pResources
);
4523 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
4524 CPPUNIT_ASSERT(pXObjects
);
4526 // Without the fix LookupObject for all /Im's will fail.
4527 for (auto const& rPair
: pXObjects
->GetItems())
4529 if (rPair
.first
.startsWith("Im"))
4530 CPPUNIT_ASSERT(pXObjects
->LookupObject(rPair
.first
));
4534 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testRexportFilterSingletonArray
)
4536 // We need to enable PDFium import (and make sure to disable after the test)
4537 bool bResetEnvVar
= false;
4538 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
4540 bResetEnvVar
= true;
4541 osl_setEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
, u
"1"_ustr
.pData
);
4543 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
4545 osl_clearEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
);
4548 // the test fails with tagged PDF enabled
4549 uno::Sequence
<beans::PropertyValue
> aFilterData(
4550 comphelper::InitPropertySequence({ { "UseTaggedPDF", uno::Any(false) } }));
4551 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
4553 // Load the PDF and save as PDF
4554 vcl::filter::PDFDocument aDocument
;
4555 load(u
"ref-to-kids.pdf", aDocument
);
4557 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
4558 CPPUNIT_ASSERT_EQUAL(size_t(5), aPages
.size());
4560 // Directly go to the inner XObject Im5 that contains the rectangle drawings.
4561 auto pInnerIm
= aDocument
.LookupObject(5);
4562 CPPUNIT_ASSERT(pInnerIm
);
4564 auto pFilter
= dynamic_cast<vcl::filter::PDFNameElement
*>(pInnerIm
->Lookup("Filter"_ostr
));
4565 CPPUNIT_ASSERT(pFilter
);
4566 CPPUNIT_ASSERT_EQUAL_MESSAGE("Filter must be FlateDecode", "FlateDecode"_ostr
,
4567 pFilter
->GetValue());
4569 vcl::filter::PDFStreamElement
* pStream
= pInnerIm
->GetStream();
4570 CPPUNIT_ASSERT(pStream
);
4571 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
4573 SvMemoryStream aUncompressed
;
4575 aZCodec
.BeginCompression();
4576 rObjectStream
.Seek(0);
4577 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
4578 CPPUNIT_ASSERT(aZCodec
.EndCompression());
4580 // Without the fix, the stream is doubly compressed,
4581 // hence one decompression will not yield the "re" expressions.
4582 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
4583 const char* pEnd
= pStart
+ aUncompressed
.GetSize();
4584 OString aImage
= "100 0 30 50 re B*\n70 67 50 30 re B*\n"_ostr
;
4585 auto it
= std::search(pStart
, pEnd
, aImage
.getStr(), aImage
.getStr() + aImage
.getLength());
4586 CPPUNIT_ASSERT(it
!= pEnd
);
4589 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testRexportMediaBoxOrigin
)
4591 // We need to enable PDFium import (and make sure to disable after the test)
4592 bool bResetEnvVar
= false;
4593 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
4595 bResetEnvVar
= true;
4596 osl_setEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
, u
"1"_ustr
.pData
);
4598 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
4600 osl_clearEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
);
4603 // Load the PDF and save as PDF
4604 vcl::filter::PDFDocument aDocument
;
4605 load(u
"ref-to-kids.pdf", aDocument
);
4607 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
4608 CPPUNIT_ASSERT_EQUAL(size_t(5), aPages
.size());
4610 // Directly go to the inner XObject Im12 that contains the rectangle drawings in page 2.
4611 auto pInnerIm
= aDocument
.LookupObject(12);
4612 CPPUNIT_ASSERT(pInnerIm
);
4614 static constexpr sal_Int32 aOrigin
[2] = { -800, -600 };
4615 sal_Int32 aSize
[2] = { 0, 0 };
4617 auto pBBox
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pInnerIm
->Lookup("BBox"_ostr
));
4618 CPPUNIT_ASSERT(pBBox
);
4619 const auto& rElements2
= pBBox
->GetElements();
4620 CPPUNIT_ASSERT_EQUAL(size_t(4), rElements2
.size());
4621 for (sal_Int32 nIdx
= 0; nIdx
< 4; ++nIdx
)
4623 const auto* pNumElement
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements2
[nIdx
]);
4624 CPPUNIT_ASSERT(pNumElement
);
4626 CPPUNIT_ASSERT_EQUAL(aOrigin
[nIdx
], static_cast<sal_Int32
>(pNumElement
->GetValue()));
4628 aSize
[nIdx
- 2] = static_cast<sal_Int32
>(pNumElement
->GetValue()) - aOrigin
[nIdx
- 2];
4631 auto pMatrix
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pInnerIm
->Lookup("Matrix"_ostr
));
4632 CPPUNIT_ASSERT(pMatrix
);
4633 const auto& rElements
= pMatrix
->GetElements();
4634 CPPUNIT_ASSERT_EQUAL(size_t(6), rElements
.size());
4635 sal_Int32 aMatTranslate
[6]
4636 = { // Rotation by $\theta$ $cos(\theta), sin(\theta), -sin(\theta), cos(\theta)$
4639 -aOrigin
[1] - aSize
[1] / vcl::PDF_INSERT_MAGIC_SCALE_FACTOR
/ 2
4640 + aSize
[0] / vcl::PDF_INSERT_MAGIC_SCALE_FACTOR
/ 2,
4641 aOrigin
[0] + aSize
[0] / vcl::PDF_INSERT_MAGIC_SCALE_FACTOR
/ 2
4642 + aSize
[1] / vcl::PDF_INSERT_MAGIC_SCALE_FACTOR
/ 2
4645 for (sal_Int32 nIdx
= 0; nIdx
< 6; ++nIdx
)
4647 const auto* pNumElement
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[nIdx
]);
4648 CPPUNIT_ASSERT(pNumElement
);
4649 CPPUNIT_ASSERT_EQUAL(aMatTranslate
[nIdx
], static_cast<sal_Int32
>(pNumElement
->GetValue()));
4653 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testRexportResourceItemReference
)
4655 // We need to enable PDFium import (and make sure to disable after the test)
4656 bool bResetEnvVar
= false;
4657 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
4659 bResetEnvVar
= true;
4660 osl_setEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
, u
"1"_ustr
.pData
);
4662 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
4664 osl_clearEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
);
4667 // Load the PDF and save as PDF
4668 vcl::filter::PDFDocument aDocument
;
4669 load(u
"ref-to-kids.pdf", aDocument
);
4671 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
4672 CPPUNIT_ASSERT_EQUAL(size_t(5), aPages
.size());
4674 // Directly go to the inner XObject Im12 that has reference to Font in page 2.
4675 auto pInnerIm
= aDocument
.LookupObject(12);
4676 CPPUNIT_ASSERT(pInnerIm
);
4679 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pInnerIm
->Lookup("Resources"_ostr
));
4680 CPPUNIT_ASSERT(pResources
);
4681 auto pFontsReference
4682 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pResources
->LookupElement("Font"_ostr
));
4683 CPPUNIT_ASSERT(pFontsReference
);
4685 auto pFontsObject
= pFontsReference
->LookupObject();
4686 CPPUNIT_ASSERT(pFontsObject
);
4689 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pFontsObject
->Lookup("FF132"_ostr
));
4690 CPPUNIT_ASSERT(pFontDict
);
4692 auto pFontDescriptor
= pFontDict
->LookupObject("FontDescriptor"_ostr
);
4693 CPPUNIT_ASSERT(pFontDescriptor
);
4695 auto pFontWidths
= pFontDict
->LookupObject("Widths"_ostr
);
4696 CPPUNIT_ASSERT(pFontWidths
);
4699 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf152246
)
4701 // Import the bugdoc and export as PDF.
4702 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
4703 { "ExportFormFields", uno::Any(true) },
4705 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
4706 vcl::filter::PDFDocument aDocument
;
4707 load(u
"content-control-rtl.docx", aDocument
);
4709 // The document has one page.
4710 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
4711 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
4714 static constexpr double aPos
[5][4] = { { 55.699, 706.701, 132.401, 722.499 },
4715 { 197.499, 706.701, 274.201, 722.499 },
4716 { 302.349, 679.101, 379.051, 694.899 },
4717 { 479.599, 679.101, 556.301, 694.899 },
4718 { 55.699, 651.501, 132.401, 667.299 } };
4720 // Get page annotations.
4721 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
4722 CPPUNIT_ASSERT(pAnnots
);
4723 CPPUNIT_ASSERT_EQUAL(size_t(5), pAnnots
->GetElements().size());
4724 for (sal_Int32 i
= 0; i
< 5; ++i
)
4726 auto pAnnotReference
4727 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pAnnots
->GetElements()[i
]);
4728 CPPUNIT_ASSERT(pAnnotReference
);
4729 vcl::filter::PDFObjectElement
* pAnnot
= pAnnotReference
->LookupObject();
4730 CPPUNIT_ASSERT(pAnnot
);
4731 CPPUNIT_ASSERT_EQUAL(
4733 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
))->GetValue());
4734 CPPUNIT_ASSERT_EQUAL(
4736 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
))->GetValue());
4738 auto pRect
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
4739 CPPUNIT_ASSERT(pRect
);
4740 const auto& rElements
= pRect
->GetElements();
4741 CPPUNIT_ASSERT_EQUAL(size_t(4), rElements
.size());
4742 for (sal_Int32 nIdx
= 0; nIdx
< 4; ++nIdx
)
4744 const auto* pNumElement
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[nIdx
]);
4745 CPPUNIT_ASSERT(pNumElement
);
4746 CPPUNIT_ASSERT_DOUBLES_EQUAL(aPos
[i
][nIdx
], pNumElement
->GetValue(), 1e-6);
4751 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf155161
)
4753 // TODO: We seem to get a fallback font on Windows
4755 vcl::filter::PDFDocument aDocument
;
4756 load(u
"tdf155161.odt", aDocument
);
4758 // Check that all fonts in the document are Type 3 fonts
4760 for (const auto& aElement
: aDocument
.GetElements())
4762 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
4765 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
4766 if (pType
&& pType
->GetValue() == "Font")
4769 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Subtype"_ostr
));
4770 CPPUNIT_ASSERT(pSubtype
);
4771 CPPUNIT_ASSERT_EQUAL("Type3"_ostr
, pSubtype
->GetValue());
4773 auto pName
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Name"_ostr
));
4774 CPPUNIT_ASSERT(pName
);
4775 CPPUNIT_ASSERT_EQUAL("Cantarell-Regular"_ostr
, pName
->GetValue());
4782 // There must be two fonts
4783 CPPUNIT_ASSERT_EQUAL(2, nFonts
);
4785 // But it seems that embedded variable fonts don’t register all supported
4786 // styles on Linux, so the bold and regular text use the same regular font.
4787 CPPUNIT_ASSERT(nFonts
);
4792 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf48707_1
)
4794 // Import the bugdoc and export as PDF.
4795 saveAsPDF(u
"tdf48707-1.fodt");
4797 // Parse the export result with pdfium.
4798 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4800 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4802 auto pPage
= pPdfDocument
->openPage(0);
4803 CPPUNIT_ASSERT(pPage
);
4805 int nPageObjectCount
= pPage
->getObjectCount();
4807 CPPUNIT_ASSERT_EQUAL(6, nPageObjectCount
);
4809 auto pTextPage
= pPage
->getTextPage();
4811 for (int i
= 0; i
< nPageObjectCount
; ++i
)
4813 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPage
->getObject(i
);
4814 // The text and path objects (underline and overline) should all be red.
4815 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
4816 CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED
, pPageObject
->getFillColor());
4818 CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED
, pPageObject
->getStrokeColor());
4822 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf48707_2
)
4824 // Import the bugdoc and export as PDF.
4825 saveAsPDF(u
"tdf48707-2.fodt");
4827 // Parse the export result with pdfium.
4828 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4830 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4832 auto pPage
= pPdfDocument
->openPage(0);
4833 CPPUNIT_ASSERT(pPage
);
4835 int nPageObjectCount
= pPage
->getObjectCount();
4837 CPPUNIT_ASSERT_EQUAL(13, nPageObjectCount
);
4839 auto pTextPage
= pPage
->getTextPage();
4841 for (int i
= 0; i
< nPageObjectCount
; ++i
)
4843 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPage
->getObject(i
);
4844 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Path
)
4847 // The table-like paths should be red, underline and overline should be black.
4849 CPPUNIT_ASSERT_EQUAL(COL_BLACK
, pPageObject
->getStrokeColor());
4851 CPPUNIT_ASSERT_EQUAL(COL_LIGHTRED
, pPageObject
->getStrokeColor());
4855 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf156528
)
4857 saveAsPDF(u
"wide_page1.fodt");
4858 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4860 // The document has two pages
4861 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
4863 // 1st page (5100 mm width x 210 mm high, UserUnit = 2)
4864 auto pPdfPage
= pPdfDocument
->openPage(0);
4865 CPPUNIT_ASSERT(pPdfPage
);
4866 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5100.0 / 2, o3tl::Length::mm
, o3tl::Length::pt
),
4867 pPdfPage
->getWidth(), 1);
4868 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0 / 2, o3tl::Length::mm
, o3tl::Length::pt
),
4869 pPdfPage
->getHeight(), 1);
4871 // 1 object (rectangle 5060 mm width x 170 mm high, UserUnit = 2)
4872 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getObjectCount());
4873 auto pRect
= pPdfPage
->getObject(0);
4874 CPPUNIT_ASSERT(pRect
);
4875 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path
, pRect
->getType());
4876 auto bounds
= pRect
->getBounds();
4877 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(5060.0 / 2, o3tl::Length::mm
, o3tl::Length::pt
),
4878 bounds
.getWidth(), 1);
4879 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0 / 2, o3tl::Length::mm
, o3tl::Length::pt
),
4880 bounds
.getHeight(), 1);
4882 // 2nd page (210 mm width x 297 mm high, UserUnit = 1)
4883 pPdfPage
= pPdfDocument
->openPage(1);
4884 CPPUNIT_ASSERT(pPdfPage
);
4885 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(210.0, o3tl::Length::mm
, o3tl::Length::pt
),
4886 pPdfPage
->getWidth(), 1);
4887 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(297.0, o3tl::Length::mm
, o3tl::Length::pt
),
4888 pPdfPage
->getHeight(), 1);
4890 // 1 object (rectangle 170 mm width x 257 mm high, UserUnit = 1)
4891 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getObjectCount());
4892 pRect
= pPdfPage
->getObject(0);
4893 CPPUNIT_ASSERT(pRect
);
4894 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Path
, pRect
->getType());
4895 bounds
= pRect
->getBounds();
4896 // Without the fix, this would fail with
4897 // - Expected: 481.889763779528
4898 // - Actual : 241.925001144409
4900 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(170.0, o3tl::Length::mm
, o3tl::Length::pt
),
4901 bounds
.getWidth(), 1);
4903 // - Expected: 728.503937007874
4904 // - Actual : 365.25
4906 CPPUNIT_ASSERT_DOUBLES_EQUAL(o3tl::convert(257.0, o3tl::Length::mm
, o3tl::Length::pt
),
4907 bounds
.getHeight(), 1);
4910 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf113866
)
4912 loadFromFile(u
"tdf113866.odt");
4914 // Set -- Printer Settings->Options->Print text in Black -- to true
4915 SwXTextDocument
* pTextDoc
= dynamic_cast<SwXTextDocument
*>(mxComponent
.get());
4916 SwDoc
* pDoc
= pTextDoc
->GetDocShell()->GetDoc();
4917 IDocumentDeviceAccess
& rDocAccess
= pDoc
->getIDocumentDeviceAccess();
4918 SwPrintData aDocPrintData
= rDocAccess
.getPrintData();
4919 aDocPrintData
.SetPrintBlackFont(true);
4920 rDocAccess
.setPrintData(aDocPrintData
);
4923 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
4924 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
4926 // Parse the export result with pdfium.
4927 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4929 // Non-NULL pPdfDocument means pdfium is available.
4930 if (pPdfDocument
!= nullptr)
4932 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(0);
4933 CPPUNIT_ASSERT(pPdfPage
);
4935 int nPageObjectCount
= pPdfPage
->getObjectCount();
4936 for (int i
= 0; i
< nPageObjectCount
; ++i
)
4938 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
4940 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
4941 // Without the bug fix in place the test will fail with
4942 // - Expected: rgba[008000ff]
4943 // - Actual : rgba[000000ff]
4944 // With the bug fixed, the green text in the test doc will stay green,
4945 // when exported to pdf, while Print Text in Black is true
4946 CPPUNIT_ASSERT_EQUAL(COL_GREEN
, pPageObject
->getFillColor());
4951 // Form controls coordinates scrambled when exporting to pdf with unchecked form creation in Writer
4952 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf159817
)
4955 uno::Sequence
<beans::PropertyValue
> aFilterData(
4956 comphelper::InitPropertySequence({ { "ExportFormFields", uno::Any(false) } }));
4957 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
4958 saveAsPDF(u
"tdf159817.fodt");
4960 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
4962 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
4963 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
4964 CPPUNIT_ASSERT(pPdfPage
);
4965 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
4966 CPPUNIT_ASSERT(pTextPage
);
4968 // So I extracted these values by using SAL_WARN(... << ...getMinimum()), but it appears
4969 // that the C++ stream operators do not output double values with sufficient resolution for me
4970 // to recreate those values in code, sigh, so resort to rounding things.
4971 auto roundPoint
= [&pPdfPage
](int i
) {
4972 auto p
= pPdfPage
->getObject(i
)->getBounds().getMinimum();
4973 return basegfx::B2DPoint(std::floor(p
.getX() * 10) / 10.0,
4974 std::floor(p
.getY() * 10) / 10.0);
4976 // before the fix these co-ordinates would have been way further down the page
4977 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(8.6, 677.3), roundPoint(13));
4978 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(9.3, 677.9), roundPoint(14));
4979 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(9.8, 678.5), roundPoint(15));
4980 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(85.0, 677.3), roundPoint(16));
4981 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(85.6, 677.9), roundPoint(17));
4982 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(170.1, 677.3), roundPoint(18));
4983 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(170.6, 677.9), roundPoint(19));
4984 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(0.0, 654.0), roundPoint(20));
4985 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(0.6, 654.6), roundPoint(21));
4986 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.3, 655.5), roundPoint(22));
4987 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.3, 655.5), roundPoint(23));
4988 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.2, 655.5), roundPoint(24));
4989 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.2, 655.5), roundPoint(25));
4990 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.4, 655.5), roundPoint(26));
4991 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.4, 655.5), roundPoint(27));
4992 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.1, 655.5), roundPoint(28));
4993 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.1, 655.5), roundPoint(29));
4994 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.5, 655.5), roundPoint(30));
4995 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.5, 655.5), roundPoint(31));
4996 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.0, 655.5), roundPoint(32));
4997 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(1.0, 655.5), roundPoint(33));
4998 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(28.3, 641.4), roundPoint(34));
4999 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(28.3, 623.7), roundPoint(35));
5000 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(28.3, 623.8), roundPoint(36));
5001 CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(138.6, 623.7), roundPoint(37));
5004 // Tests that kerning is correctly applied across color changes
5005 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf61444
)
5007 saveAsPDF(u
"tdf61444.odt");
5008 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
5010 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5012 // Get the first page
5013 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5014 CPPUNIT_ASSERT(pPdfPage
);
5015 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
5016 CPPUNIT_ASSERT(pTextPage
);
5018 // 4 text objects should be present
5019 int nPageObjectCount
= pPdfPage
->getObjectCount();
5020 CPPUNIT_ASSERT_EQUAL(4, nPageObjectCount
);
5023 basegfx::B2DRectangle aRect
[4];
5025 int nTextObjectCount
= 0;
5026 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5028 auto pPageObject
= pPdfPage
->getObject(i
);
5029 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5030 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5032 sText
[nTextObjectCount
] = pPageObject
->getText(pTextPage
);
5033 aRect
[nTextObjectCount
] = pPageObject
->getBounds();
5038 CPPUNIT_ASSERT_EQUAL(4, nTextObjectCount
);
5040 CPPUNIT_ASSERT_EQUAL(u
"Wait"_ustr
, sText
[0].trim());
5041 CPPUNIT_ASSERT_EQUAL(u
"W"_ustr
, sText
[1].trim());
5042 CPPUNIT_ASSERT_EQUAL(u
"ai"_ustr
, sText
[2].trim());
5043 CPPUNIT_ASSERT_EQUAL(u
"t"_ustr
, sText
[3].trim());
5045 // Both lines should have the same kerning, so should end at approximately the same X coordinate
5046 auto solid_extent
= aRect
[0].getMaxX();
5047 auto color_extent
= aRect
[3].getMaxX();
5049 CPPUNIT_ASSERT_DOUBLES_EQUAL(solid_extent
, color_extent
, /*delta*/ 0.15);
5052 // tdf#124116 - Tests that track-changes inside a grapheme cluster does not break positioning
5053 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf124116TrackUntrack
)
5055 saveAsPDF(u
"tdf124116-hebrew-track-untrack.odt");
5056 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
5058 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5060 // Get the first page
5061 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5062 CPPUNIT_ASSERT(pPdfPage
);
5063 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
5064 CPPUNIT_ASSERT(pTextPage
);
5066 int nPageObjectCount
= pPdfPage
->getObjectCount();
5067 CPPUNIT_ASSERT_EQUAL(15, nPageObjectCount
);
5069 std::vector
<OUString
> aText
;
5070 std::vector
<basegfx::B2DRectangle
> aRect
;
5072 int nTextObjectCount
= 0;
5073 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5075 auto pPageObject
= pPdfPage
->getObject(i
);
5076 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5077 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5079 aText
.push_back(pPageObject
->getText(pTextPage
));
5080 aRect
.push_back(pPageObject
->getBounds());
5085 // The underlying document has 4 lines:
5088 // - שמחַ with patah tracked
5089 // - שמחַ with everything except patah tracked
5091 // However, due to the way text items are inserted for Hebrew, there will be 10:
5092 // - het with an improperly spaced patah, then שמ for the first 2 lines
5093 // - as above, followed by a blank for the next 2 representing the actual diacritic
5095 // This test will likely need to be rewritten if tdf#158329 is fixed.
5096 CPPUNIT_ASSERT_EQUAL(10, nTextObjectCount
);
5098 // All that matters for this test is that the patah is positioned well under the het
5099 auto het_x0
= aRect
.at(4).getMinX();
5100 auto patah_x0
= aRect
.at(6).getMinX();
5101 CPPUNIT_ASSERT_GREATER(10.0, patah_x0
- het_x0
);
5103 auto het_x1
= aRect
.at(7).getMinX();
5104 auto patah_x1
= aRect
.at(9).getMinX();
5105 CPPUNIT_ASSERT_GREATER(10.0, patah_x1
- het_x1
);
5108 // tdf#134226 - Tests that shaping is not broken by invisible spans
5109 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf134226
)
5111 saveAsPDF(u
"tdf134226-shadda-in-hidden-span.fodt");
5112 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
5114 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5116 // Get the first page
5117 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5118 CPPUNIT_ASSERT(pPdfPage
);
5119 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
5120 CPPUNIT_ASSERT(pTextPage
);
5122 int nPageObjectCount
= pPdfPage
->getObjectCount();
5123 CPPUNIT_ASSERT_EQUAL(8, nPageObjectCount
);
5125 std::vector
<OUString
> aText
;
5126 std::vector
<basegfx::B2DRectangle
> aRect
;
5128 int nTextObjectCount
= 0;
5129 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5131 auto pPageObject
= pPdfPage
->getObject(i
);
5132 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5133 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5135 aText
.push_back(pPageObject
->getText(pTextPage
));
5136 aRect
.push_back(pPageObject
->getBounds());
5141 CPPUNIT_ASSERT_EQUAL(8, nTextObjectCount
);
5143 CPPUNIT_ASSERT_EQUAL(u
"ة"_ustr
, aText
[0].trim());
5144 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[1].trim());
5145 CPPUNIT_ASSERT_EQUAL(u
"\u0651ق"_ustr
, aText
[2].trim());
5146 CPPUNIT_ASSERT_EQUAL(u
"ش"_ustr
, aText
[3].trim());
5147 CPPUNIT_ASSERT_EQUAL(u
"\u0651ق"_ustr
, aText
[4].trim());
5148 CPPUNIT_ASSERT_EQUAL(u
"ش"_ustr
, aText
[5].trim());
5149 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[6].trim());
5150 CPPUNIT_ASSERT_EQUAL(u
"ة"_ustr
, aText
[7].trim());
5152 // Verify that the corresponding text segments are positioned roughly equally
5154 = [](const basegfx::B2DRectangle
& stExpected
, const basegfx::B2DRectangle
& stFound
) {
5155 CPPUNIT_ASSERT_DOUBLES_EQUAL(stExpected
.getMinX(), stFound
.getMinX(), /*delta*/ 0.15);
5156 CPPUNIT_ASSERT_DOUBLES_EQUAL(stExpected
.getMaxX(), stFound
.getMaxX(), /*delta*/ 0.15);
5159 fnEqualPos(aRect
[0], aRect
[7]);
5160 fnEqualPos(aRect
[1], aRect
[6]);
5161 fnEqualPos(aRect
[2], aRect
[4]);
5162 fnEqualPos(aRect
[3], aRect
[5]);
5165 // tdf#71956 - Tests that glyphs can be individually styled
5166 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf71956
)
5168 saveAsPDF(u
"tdf71956-styled-diacritics.fodt");
5169 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
5171 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5173 // Get the first page
5174 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5175 CPPUNIT_ASSERT(pPdfPage
);
5176 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
5177 CPPUNIT_ASSERT(pTextPage
);
5179 int nPageObjectCount
= pPdfPage
->getObjectCount();
5180 CPPUNIT_ASSERT_EQUAL(12, nPageObjectCount
);
5182 std::vector
<OUString
> aText
;
5183 std::vector
<basegfx::B2DRectangle
> aRect
;
5185 int nTextObjectCount
= 0;
5186 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5188 auto pPageObject
= pPdfPage
->getObject(i
);
5189 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5190 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5192 aText
.push_back(pPageObject
->getText(pTextPage
));
5193 aRect
.push_back(pPageObject
->getBounds());
5198 CPPUNIT_ASSERT_EQUAL(12, nTextObjectCount
);
5200 CPPUNIT_ASSERT_EQUAL(u
"ه"_ustr
, aText
[0].trim());
5201 CPPUNIT_ASSERT_EQUAL(u
"\u0670\u0020\u0644\u0644"_ustr
, aText
[1].trim());
5202 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[2].trim());
5203 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[3].trim());
5204 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[4].trim());
5205 CPPUNIT_ASSERT_EQUAL(u
"ل"_ustr
, aText
[5].trim());
5206 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[6].trim());
5207 CPPUNIT_ASSERT_EQUAL(u
"\u0647"_ustr
, aText
[7].trim());
5208 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[8].trim());
5209 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[9].trim());
5210 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[10].trim());
5211 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
[11].trim());
5213 // Verify that the corresponding text segments are positioned roughly equally
5215 = [](const basegfx::B2DRectangle
& stExpected
, const basegfx::B2DRectangle
& stFound
) {
5216 CPPUNIT_ASSERT_DOUBLES_EQUAL(stExpected
.getMinX(), stFound
.getMinX(), /*delta*/ 0.15);
5217 CPPUNIT_ASSERT_DOUBLES_EQUAL(stExpected
.getMaxX(), stFound
.getMaxX(), /*delta*/ 0.15);
5220 fnEqualPos(aRect
[0], aRect
[11]);
5221 fnEqualPos(aRect
[1], aRect
[10]);
5222 fnEqualPos(aRect
[2], aRect
[8]);
5223 fnEqualPos(aRect
[3], aRect
[9]);
5224 fnEqualPos(aRect
[4], aRect
[7]);
5225 fnEqualPos(aRect
[5], aRect
[6]);
5228 // tdf#101686 - Verifies that drawinglayer clears RTL flags while drawing Writer text boxes
5229 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf101686
)
5231 saveAsPDF(u
"tdf101686.fodt");
5232 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
5234 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5236 // Get the first pace
5237 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5238 CPPUNIT_ASSERT(pPdfPage
);
5239 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
5240 CPPUNIT_ASSERT(pTextPage
);
5242 int nPageObjectCount
= pPdfPage
->getObjectCount();
5243 CPPUNIT_ASSERT_EQUAL(3, nPageObjectCount
);
5245 std::vector
<OUString
> aText
;
5247 int nTextObjectCount
= 0;
5248 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5250 auto pPageObject
= pPdfPage
->getObject(i
);
5251 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5252 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5254 aText
.push_back(pPageObject
->getText(pTextPage
));
5259 CPPUNIT_ASSERT_EQUAL(3, nTextObjectCount
);
5261 CPPUNIT_ASSERT_EQUAL(u
"Frame"_ustr
, aText
[0].trim());
5263 // Without the fix, one of these two will be "xobtxeT"
5264 CPPUNIT_ASSERT_EQUAL(u
"Textbox"_ustr
, aText
[1].trim());
5265 CPPUNIT_ASSERT_EQUAL(u
"Textbox"_ustr
, aText
[2].trim());
5268 // tdf#162161 reexport appears to have blank image
5269 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testRexportXnViewColorspace
)
5271 // We need to enable PDFium import (and make sure to disable after the test)
5272 bool bResetEnvVar
= false;
5273 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
5275 bResetEnvVar
= true;
5276 osl_setEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
, u
"1"_ustr
.pData
);
5278 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
5280 osl_clearEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
);
5283 // Load the PDF and save as PDF
5284 vcl::filter::PDFDocument aDocument
;
5285 load(u
"xnview-colorspace.pdf", aDocument
);
5287 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
5288 CPPUNIT_ASSERT_EQUAL(size_t(1), aPages
.size());
5290 // Get access to the only image on the only page.
5291 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
5292 CPPUNIT_ASSERT(pResources
);
5295 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
5296 CPPUNIT_ASSERT(pXObjects
);
5297 CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects
->GetItems().size());
5298 vcl::filter::PDFObjectElement
* pXObject
5299 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
5300 CPPUNIT_ASSERT(pXObject
);
5303 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject
->Lookup("Resources"_ostr
));
5304 CPPUNIT_ASSERT(pSubResources
);
5305 pXObjects
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
5306 pSubResources
->LookupElement("XObject"_ostr
));
5307 CPPUNIT_ASSERT(pXObjects
);
5308 CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects
->GetItems().size());
5309 pXObject
= pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
5310 CPPUNIT_ASSERT(pXObject
);
5313 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject
->Lookup("Resources"_ostr
));
5314 CPPUNIT_ASSERT(pSubResources
);
5315 pXObjects
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
5316 pSubResources
->LookupElement("XObject"_ostr
));
5317 CPPUNIT_ASSERT(pXObjects
);
5318 CPPUNIT_ASSERT_EQUAL(size_t(1), pXObjects
->GetItems().size());
5319 pXObject
= pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
5320 CPPUNIT_ASSERT(pXObject
);
5322 // Dig all the way down to this element which is originally
5326 // and appeared blank when we lost the /DeviceRGB line
5327 auto pColorspace
= pXObject
->LookupObject("ColorSpace"_ostr
);
5328 CPPUNIT_ASSERT(pColorspace
);
5329 auto pColorSpaceElement
= pColorspace
->GetNameElement();
5330 CPPUNIT_ASSERT(pColorSpaceElement
);
5331 CPPUNIT_ASSERT_EQUAL("DeviceRGB"_ostr
, pColorSpaceElement
->GetValue());
5334 // tdf#157390 - Verifies metrics are correct for PDF export mixing horizontal and vertical CJK
5335 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf157390
)
5337 saveAsPDF(u
"tdf157390-overlapping-kanji.fodt");
5339 auto pPdfDocument
= parsePDFExport();
5340 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5342 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5343 CPPUNIT_ASSERT(pPdfPage
);
5345 auto pTextPage
= pPdfPage
->getTextPage();
5346 CPPUNIT_ASSERT(pTextPage
);
5348 // This bug manifests as aberrant character advances in the middle horizontal text.
5350 // Locate the text on the page
5351 auto aStr
= u
"無い有る有る"_ustr
;
5354 for (int i
= 0; i
< pTextPage
->countChars(); ++i
)
5356 if (pTextPage
->getUnicode(i
) == static_cast<unsigned int>(aStr
[0]))
5363 CPPUNIT_ASSERT(nBaseIndex
+ 6 <= pTextPage
->countChars());
5365 // Extract the character rects
5366 std::vector
<basegfx::B2DRectangle
> aRects
;
5367 for (int i
= 0; i
< 6; ++i
)
5369 auto nPageIndex
= nBaseIndex
+ i
;
5370 CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(aStr
[i
]), pTextPage
->getUnicode(nPageIndex
));
5371 aRects
.push_back(pTextPage
->getCharBox(nPageIndex
, /*fPageHeight*/ 1000.0));
5374 // Verify glyph advances don't exceed some pessimistic range
5375 double nGuess
= aRects
.at(0).getMinX();
5376 for (const auto& stRect
: aRects
)
5378 std::cout
<< stRect
<< std::endl
;
5380 CPPUNIT_ASSERT_GREATER(nGuess
- 0.1 * stRect
.getWidth(), stRect
.getMinX());
5381 CPPUNIT_ASSERT_LESS(nGuess
+ 0.5 * stRect
.getWidth(), stRect
.getMinX());
5383 nGuess
= stRect
.getMaxX();
5387 // tdf#162205 - Verifies bidi portions on vertical left-to-right pages are rendered correctly
5388 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf162205Ltr
)
5390 saveAsPDF(u
"tdf162205-ltr.fodt");
5392 auto pPdfDocument
= parsePDFExport();
5393 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5395 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5396 CPPUNIT_ASSERT(pPdfPage
);
5397 auto pTextPage
= pPdfPage
->getTextPage();
5398 CPPUNIT_ASSERT(pTextPage
);
5400 int nPageObjectCount
= pPdfPage
->getObjectCount();
5401 CPPUNIT_ASSERT_EQUAL(10, nPageObjectCount
);
5403 std::vector
<OUString
> aText
;
5404 std::vector
<basegfx::B2DRectangle
> aRect
;
5406 int nTextObjectCount
= 0;
5407 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5409 auto pPageObject
= pPdfPage
->getObject(i
);
5410 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5411 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5413 aText
.push_back(pPageObject
->getText(pTextPage
));
5414 aRect
.push_back(pPageObject
->getBounds());
5419 CPPUNIT_ASSERT_EQUAL(10, nTextObjectCount
);
5421 CPPUNIT_ASSERT_EQUAL(u
"T"_ustr
, aText
.at(0).trim());
5422 CPPUNIT_ASSERT_EQUAL(u
"h"_ustr
, aText
.at(1).trim());
5423 CPPUNIT_ASSERT_EQUAL(u
"e"_ustr
, aText
.at(2).trim());
5424 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(3).trim());
5425 CPPUNIT_ASSERT_EQUAL(u
"\u0644"_ustr
, aText
.at(4).trim()); // lam
5426 CPPUNIT_ASSERT_EQUAL(u
"\u0627"_ustr
, aText
.at(5).trim()); // alef
5427 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(6).trim());
5428 CPPUNIT_ASSERT_EQUAL(u
"T"_ustr
, aText
.at(7).trim());
5429 CPPUNIT_ASSERT_EQUAL(u
"h"_ustr
, aText
.at(8).trim());
5430 CPPUNIT_ASSERT_EQUAL(u
"e"_ustr
, aText
.at(9).trim());
5432 // When the bug occurs, the Arabic portion is rendered far to the left of the English portions.
5433 // Verify that Arabic characters are within range.
5434 auto fnWithinRange
= [](const auto& stExpected
, const auto& stFound
) {
5435 CPPUNIT_ASSERT_LESS(20.0, std::abs(stExpected
.getMinX() - stFound
.getMinX()));
5438 fnWithinRange(aRect
.at(0), aRect
.at(4));
5439 fnWithinRange(aRect
.at(7), aRect
.at(5));
5442 // tdf#162205 - Verifies bidi portions on vertical left-to-right pages are rendered correctly
5443 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf162205Rtl
)
5445 saveAsPDF(u
"tdf162205-rtl.fodt");
5447 auto pPdfDocument
= parsePDFExport();
5448 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5450 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5451 CPPUNIT_ASSERT(pPdfPage
);
5452 auto pTextPage
= pPdfPage
->getTextPage();
5453 CPPUNIT_ASSERT(pTextPage
);
5455 int nPageObjectCount
= pPdfPage
->getObjectCount();
5456 CPPUNIT_ASSERT_EQUAL(10, nPageObjectCount
);
5458 std::vector
<OUString
> aText
;
5459 std::vector
<basegfx::B2DRectangle
> aRect
;
5461 int nTextObjectCount
= 0;
5462 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5464 auto pPageObject
= pPdfPage
->getObject(i
);
5465 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5466 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5468 aText
.push_back(pPageObject
->getText(pTextPage
));
5469 aRect
.push_back(pPageObject
->getBounds());
5474 CPPUNIT_ASSERT_EQUAL(10, nTextObjectCount
);
5476 CPPUNIT_ASSERT_EQUAL(u
"T"_ustr
, aText
.at(0).trim());
5477 CPPUNIT_ASSERT_EQUAL(u
"h"_ustr
, aText
.at(1).trim());
5478 CPPUNIT_ASSERT_EQUAL(u
"e"_ustr
, aText
.at(2).trim());
5479 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(3).trim());
5480 CPPUNIT_ASSERT_EQUAL(u
"\u0644"_ustr
, aText
.at(4).trim()); // lam
5481 CPPUNIT_ASSERT_EQUAL(u
"\u0627"_ustr
, aText
.at(5).trim()); // alef
5482 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(6).trim());
5483 CPPUNIT_ASSERT_EQUAL(u
"T"_ustr
, aText
.at(7).trim());
5484 CPPUNIT_ASSERT_EQUAL(u
"h"_ustr
, aText
.at(8).trim());
5485 CPPUNIT_ASSERT_EQUAL(u
"e"_ustr
, aText
.at(9).trim());
5487 // When the bug occurs, the Arabic portion is rendered far to the left of the English portions.
5488 // Verify that Arabic characters are within range.
5489 auto fnWithinRange
= [](const auto& stExpected
, const auto& stFound
) {
5490 CPPUNIT_ASSERT_LESS(20.0, std::abs(stExpected
.getMinX() - stFound
.getMinX()));
5493 fnWithinRange(aRect
.at(0), aRect
.at(4));
5494 fnWithinRange(aRect
.at(7), aRect
.at(5));
5497 // tdf#162194 - Verifies soft hyphens inside ligatures are rendered correctly.
5498 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf162194SoftHyphen
)
5500 saveAsPDF(u
"tdf162194-soft-hyphen.fodt");
5502 auto pPdfDocument
= parsePDFExport();
5503 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5505 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5506 CPPUNIT_ASSERT(pPdfPage
);
5507 auto pTextPage
= pPdfPage
->getTextPage();
5508 CPPUNIT_ASSERT(pTextPage
);
5510 int nPageObjectCount
= pPdfPage
->getObjectCount();
5511 CPPUNIT_ASSERT_EQUAL(4, nPageObjectCount
);
5513 std::vector
<OUString
> aText
;
5514 std::vector
<basegfx::B2DRectangle
> aRect
;
5516 int nTextObjectCount
= 0;
5517 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5519 auto pPageObject
= pPdfPage
->getObject(i
);
5520 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5521 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5523 aText
.push_back(pPageObject
->getText(pTextPage
));
5524 aRect
.push_back(pPageObject
->getBounds());
5529 CPPUNIT_ASSERT_EQUAL(4, nTextObjectCount
);
5531 CPPUNIT_ASSERT_EQUAL(u
"Waffle"_ustr
, aText
.at(0).trim());
5532 CPPUNIT_ASSERT_EQUAL(u
"AAA Waf"_ustr
, aText
.at(1).trim());
5533 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(2).trim());
5534 CPPUNIT_ASSERT_EQUAL(u
"fle"_ustr
, aText
.at(3).trim());
5537 // tdf#160786 - Tests that Calc format code with repeat char is measured correctly
5538 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf160786
)
5540 saveAsPDF(u
"tdf160786.fods");
5542 auto pPdfDocument
= parsePDFExport();
5543 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5545 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5546 CPPUNIT_ASSERT(pPdfPage
);
5547 auto pTextPage
= pPdfPage
->getTextPage();
5548 CPPUNIT_ASSERT(pTextPage
);
5550 int nPageObjectCount
= pPdfPage
->getObjectCount();
5551 CPPUNIT_ASSERT_EQUAL(5, nPageObjectCount
);
5553 std::vector
<OUString
> aText
;
5554 std::vector
<basegfx::B2DRectangle
> aRect
;
5556 int nTextObjectCount
= 0;
5557 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5559 auto pPageObject
= pPdfPage
->getObject(i
);
5560 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5561 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5563 aText
.push_back(pPageObject
->getText(pTextPage
));
5564 aRect
.push_back(pPageObject
->getBounds());
5569 CPPUNIT_ASSERT_EQUAL(5, nTextObjectCount
);
5571 CPPUNIT_ASSERT_EQUAL(u
"A"_ustr
, aText
.at(3).trim());
5573 // The currency line is padded with an unknown number of 'f' characters. It doesn't matter how
5574 // many are used, as long as the cell is padded to the expected width. Just verify that this
5575 // text object is the expected one.
5576 CPPUNIT_ASSERT(o3tl::trim(aText
.at(4)).starts_with(u
"$"));
5578 // The currency cell must not overlap the adjacent cell
5579 CPPUNIT_ASSERT_GREATEREQUAL(aRect
.at(3).getMaxX(), aRect
.at(4).getMinX());
5581 // The currency cell must be padded to occupy its space reasonably well.
5582 // As a heuristic, ensure the free space is no more than the width of "A"
5583 CPPUNIT_ASSERT_LESS(aRect
.at(3).getMaxX() + aRect
.at(3).getWidth(), aRect
.at(4).getMinX());
5586 // tdf#151748 - Textboxes should validate kashida positions
5587 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf151748KashidaSpace
)
5589 saveAsPDF(u
"tdf151748.fodt");
5591 auto pPdfDocument
= parsePDFExport();
5592 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5594 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5595 CPPUNIT_ASSERT(pPdfPage
);
5596 auto pTextPage
= pPdfPage
->getTextPage();
5597 CPPUNIT_ASSERT(pTextPage
);
5599 int nPageObjectCount
= pPdfPage
->getObjectCount();
5600 CPPUNIT_ASSERT_EQUAL(21, nPageObjectCount
);
5602 std::vector
<OUString
> aText
;
5603 std::vector
<basegfx::B2DRectangle
> aRect
;
5605 int nTextObjectCount
= 0;
5606 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5608 auto pPageObject
= pPdfPage
->getObject(i
);
5609 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5610 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5612 aText
.push_back(pPageObject
->getText(pTextPage
));
5613 aRect
.push_back(pPageObject
->getBounds());
5618 CPPUNIT_ASSERT_EQUAL(17, nTextObjectCount
);
5620 // Box 1: Not enough room for kashida
5621 CPPUNIT_ASSERT_EQUAL(u
"خط تخوردگی و"_ustr
, aText
.at(0).trim());
5622 CPPUNIT_ASSERT_EQUAL(u
"توسط"_ustr
, aText
.at(1).trim());
5624 // Box 2: One kashida toward end
5625 CPPUNIT_ASSERT_EQUAL(u
"وردگی و"_ustr
, aText
.at(2).trim());
5626 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(3).trim()); // Kashida
5627 CPPUNIT_ASSERT_EQUAL(u
"خط تخ"_ustr
, aText
.at(4).trim());
5628 CPPUNIT_ASSERT_EQUAL(u
"توسط"_ustr
, aText
.at(5).trim());
5630 // Box 3: Two kashida
5631 CPPUNIT_ASSERT_EQUAL(u
"وردگی و"_ustr
, aText
.at(6).trim());
5632 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(7).trim()); // Kashida
5633 CPPUNIT_ASSERT_EQUAL(u
"ط تخ"_ustr
, aText
.at(8).trim());
5634 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(9).trim()); // Kashida
5635 CPPUNIT_ASSERT_EQUAL(u
"خ"_ustr
, aText
.at(10).trim());
5636 CPPUNIT_ASSERT_EQUAL(u
"توسط"_ustr
, aText
.at(11).trim());
5638 // Box 4: One kashida (text size change)
5639 CPPUNIT_ASSERT_EQUAL(u
"خط"_ustr
, aText
.at(12).trim());
5640 CPPUNIT_ASSERT_EQUAL(u
"وردگی و"_ustr
, aText
.at(13).trim());
5641 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(14).trim()); // Kashida
5642 CPPUNIT_ASSERT_EQUAL(u
"تخ"_ustr
, aText
.at(15).trim());
5643 CPPUNIT_ASSERT_EQUAL(u
"توسط"_ustr
, aText
.at(16).trim());
5646 // tdf#163105 - Writer kashida justification should expand spaces
5647 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf163105SwKashidaSpaceExpansion
)
5649 saveAsPDF(u
"tdf163105-kashida-spaces.fodt");
5651 auto pPdfDocument
= parsePDFExport();
5652 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5654 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5655 CPPUNIT_ASSERT(pPdfPage
);
5656 auto pTextPage
= pPdfPage
->getTextPage();
5657 CPPUNIT_ASSERT(pTextPage
);
5659 int nPageObjectCount
= pPdfPage
->getObjectCount();
5660 CPPUNIT_ASSERT_EQUAL(5, nPageObjectCount
);
5662 std::vector
<OUString
> aText
;
5663 std::vector
<basegfx::B2DRectangle
> aRect
;
5665 int nTextObjectCount
= 0;
5666 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5668 auto pPageObject
= pPdfPage
->getObject(i
);
5669 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5670 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5672 aText
.push_back(pPageObject
->getText(pTextPage
));
5673 aRect
.push_back(pPageObject
->getBounds());
5678 CPPUNIT_ASSERT_EQUAL(5, nTextObjectCount
);
5680 CPPUNIT_ASSERT_EQUAL(u
"یده"_ustr
, aText
.at(0).trim());
5681 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(1).trim());
5682 CPPUNIT_ASSERT_EQUAL(u
"ه کش"_ustr
, aText
.at(2).trim()); // This span is whitespace justified
5683 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(3).trim());
5684 CPPUNIT_ASSERT_EQUAL(u
"نویس"_ustr
, aText
.at(4).trim());
5686 // Without the fix, this will be less than 25
5687 CPPUNIT_ASSERT_GREATER(150.0, aRect
.at(2).getWidth());
5690 // tdf#163105 - Writer should use font information when choosing kashida positions
5691 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf163105Writer
)
5693 saveAsPDF(u
"tdf163105-writer.fodt");
5695 auto pPdfDocument
= parsePDFExport();
5696 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5698 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5699 CPPUNIT_ASSERT(pPdfPage
);
5700 auto pTextPage
= pPdfPage
->getTextPage();
5701 CPPUNIT_ASSERT(pTextPage
);
5703 int nPageObjectCount
= pPdfPage
->getObjectCount();
5705 // The fix allows kashida justification in this document.
5706 // Without the fix, this will be 1.
5707 CPPUNIT_ASSERT_EQUAL(5, nPageObjectCount
);
5709 std::vector
<OUString
> aText
;
5710 std::vector
<basegfx::B2DRectangle
> aRect
;
5712 int nTextObjectCount
= 0;
5713 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5715 auto pPageObject
= pPdfPage
->getObject(i
);
5716 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5717 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5719 aText
.push_back(pPageObject
->getText(pTextPage
));
5720 aRect
.push_back(pPageObject
->getBounds());
5725 CPPUNIT_ASSERT_EQUAL(5, nTextObjectCount
);
5727 CPPUNIT_ASSERT_EQUAL(u
"ارسی"_ustr
, aText
.at(0).trim());
5728 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(1).trim());
5729 CPPUNIT_ASSERT_EQUAL(u
"تن ف"_ustr
, aText
.at(2).trim()); // This span is whitespace justified
5730 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(3).trim());
5731 CPPUNIT_ASSERT_EQUAL(u
"م"_ustr
, aText
.at(4).trim());
5733 // Without the fix, this will be greater than X
5734 CPPUNIT_ASSERT_LESS(170.0, aRect
.at(2).getWidth());
5737 // tdf#163105 - Edit Engine should use font information when choosing kashida positions
5738 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf163105Editeng
)
5740 saveAsPDF(u
"tdf163105-editeng.fodt");
5742 auto pPdfDocument
= parsePDFExport();
5743 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5745 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5746 CPPUNIT_ASSERT(pPdfPage
);
5747 auto pTextPage
= pPdfPage
->getTextPage();
5748 CPPUNIT_ASSERT(pTextPage
);
5750 int nPageObjectCount
= pPdfPage
->getObjectCount();
5752 // The fix allows kashida justification in this document.
5753 // Without the fix, this will be 1.
5754 CPPUNIT_ASSERT_EQUAL(5, nPageObjectCount
);
5756 std::vector
<OUString
> aText
;
5757 std::vector
<basegfx::B2DRectangle
> aRect
;
5759 int nTextObjectCount
= 0;
5760 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5762 auto pPageObject
= pPdfPage
->getObject(i
);
5763 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5764 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5766 aText
.push_back(pPageObject
->getText(pTextPage
));
5767 aRect
.push_back(pPageObject
->getBounds());
5772 CPPUNIT_ASSERT_EQUAL(5, nTextObjectCount
);
5774 CPPUNIT_ASSERT_EQUAL(u
"ارسی"_ustr
, aText
.at(0).trim());
5775 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(1).trim());
5776 CPPUNIT_ASSERT_EQUAL(u
"تن ف"_ustr
, aText
.at(2).trim()); // This span is whitespace justified
5777 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(3).trim());
5778 CPPUNIT_ASSERT_EQUAL(u
"م"_ustr
, aText
.at(4).trim());
5780 CPPUNIT_ASSERT_LESS(170.0, aRect
.at(2).getWidth());
5783 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf140767SyriacJustification
)
5785 saveAsPDF(u
"tdf140767.odt");
5787 auto pPdfDocument
= parsePDFExport();
5788 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5790 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5791 CPPUNIT_ASSERT(pPdfPage
);
5792 auto pTextPage
= pPdfPage
->getTextPage();
5793 CPPUNIT_ASSERT(pTextPage
);
5795 int nPageObjectCount
= pPdfPage
->getObjectCount();
5797 CPPUNIT_ASSERT_EQUAL(11, nPageObjectCount
);
5799 std::vector
<OUString
> aText
;
5800 std::vector
<basegfx::B2DRectangle
> aRect
;
5802 int nTextObjectCount
= 0;
5803 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5805 auto pPageObject
= pPdfPage
->getObject(i
);
5806 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5807 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5809 aText
.push_back(pPageObject
->getText(pTextPage
));
5810 aRect
.push_back(pPageObject
->getBounds());
5815 CPPUNIT_ASSERT_EQUAL(11, nTextObjectCount
);
5817 std::cout
<< "Strings" << std::endl
;
5818 for (auto const& em
: aText
)
5820 std::cout
<< em
<< std::endl
;
5821 for (sal_Int32 i
= 0; i
< em
.getLength(); ++i
)
5823 std::cout
<< std::hex
<< static_cast<uint32_t>(em
[i
]) << " ";
5825 std::cout
<< std::endl
;
5828 CPPUNIT_ASSERT_EQUAL(u
"ܝ"_ustr
, aText
.at(0).trim());
5829 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(1).trim());
5830 CPPUNIT_ASSERT_EQUAL(u
"ܺܛ"_ustr
, aText
.at(2).trim());
5831 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(3).trim());
5832 CPPUNIT_ASSERT_EQUAL(u
"ܰܚ"_ustr
, aText
.at(4).trim());
5833 CPPUNIT_ASSERT_EQUAL(u
"ܕ"_ustr
, aText
.at(5).trim()); // This span is whitespace justified
5834 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(6).trim());
5835 CPPUNIT_ASSERT_EQUAL(u
"ܰܓ"_ustr
, aText
.at(7).trim());
5836 CPPUNIT_ASSERT_EQUAL(u
"ܒ"_ustr
, aText
.at(8).trim());
5837 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(9).trim());
5838 CPPUNIT_ASSERT_EQUAL(u
"ܰܐ"_ustr
, aText
.at(10).trim());
5840 // Without kashida justification, this space will be 224.328
5841 CPPUNIT_ASSERT_LESS(90.0, aRect
.at(5).getWidth());
5844 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf36709FirstLineIndentEm
)
5846 saveAsPDF(u
"tdf36709.fodt");
5848 auto pPdfDocument
= parsePDFExport();
5849 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5851 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5852 CPPUNIT_ASSERT(pPdfPage
);
5853 auto pTextPage
= pPdfPage
->getTextPage();
5854 CPPUNIT_ASSERT(pTextPage
);
5856 int nPageObjectCount
= pPdfPage
->getObjectCount();
5858 CPPUNIT_ASSERT_EQUAL(16, nPageObjectCount
);
5860 std::vector
<OUString
> aText
;
5861 std::vector
<basegfx::B2DRectangle
> aRect
;
5863 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5865 auto pPageObject
= pPdfPage
->getObject(i
);
5866 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5867 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5869 aText
.push_back(pPageObject
->getText(pTextPage
));
5870 aRect
.push_back(pPageObject
->getBounds());
5874 CPPUNIT_ASSERT_EQUAL(size_t(16), aText
.size());
5876 // Lines from the Writer portion
5877 CPPUNIT_ASSERT_EQUAL(u
"0 em constant size"_ustr
, aText
.at(0).trim());
5878 CPPUNIT_ASSERT_DOUBLES_EQUAL(57.256, aRect
.at(0).getMinX(), /*delta*/ 2.0);
5879 CPPUNIT_ASSERT_EQUAL(u
"1 em constant size"_ustr
, aText
.at(1).trim());
5880 CPPUNIT_ASSERT_DOUBLES_EQUAL(69.856, aRect
.at(1).getMinX(), /*delta*/ 2.0);
5881 CPPUNIT_ASSERT_EQUAL(u
"2 em constant size"_ustr
, aText
.at(2).trim());
5882 CPPUNIT_ASSERT_DOUBLES_EQUAL(81.328, aRect
.at(2).getMinX(), /*delta*/ 2.0);
5883 CPPUNIT_ASSERT_EQUAL(u
"3 em constant size"_ustr
, aText
.at(3).trim());
5884 CPPUNIT_ASSERT_DOUBLES_EQUAL(93.376, aRect
.at(3).getMinX(), /*delta*/ 2.0);
5886 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(4).trim());
5887 CPPUNIT_ASSERT_DOUBLES_EQUAL(81.328, aRect
.at(4).getMinX(), /*delta*/ 2.0);
5888 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(5).trim());
5889 CPPUNIT_ASSERT_DOUBLES_EQUAL(89.504, aRect
.at(5).getMinX(), /*delta*/ 2.0);
5890 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(6).trim());
5891 CPPUNIT_ASSERT_DOUBLES_EQUAL(97.680, aRect
.at(6).getMinX(), /*delta*/ 2.0);
5892 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(7).trim());
5893 CPPUNIT_ASSERT_DOUBLES_EQUAL(105.856, aRect
.at(7).getMinX(), /*delta*/ 2.0);
5895 // Lines from the Edit Engine portion
5896 CPPUNIT_ASSERT_EQUAL(u
"0 em constant size"_ustr
, aText
.at(8).trim());
5897 CPPUNIT_ASSERT_DOUBLES_EQUAL(62.106, aRect
.at(8).getMinX(), /*delta*/ 2.0);
5898 CPPUNIT_ASSERT_EQUAL(u
"1 em constant size"_ustr
, aText
.at(9).trim());
5899 CPPUNIT_ASSERT_DOUBLES_EQUAL(76.010, aRect
.at(9).getMinX(), /*delta*/ 2.0);
5900 CPPUNIT_ASSERT_EQUAL(u
"2 em constant size"_ustr
, aText
.at(10).trim());
5901 CPPUNIT_ASSERT_DOUBLES_EQUAL(88.778, aRect
.at(10).getMinX(), /*delta*/ 2.0);
5902 CPPUNIT_ASSERT_EQUAL(u
"3 em constant size"_ustr
, aText
.at(11).trim());
5903 CPPUNIT_ASSERT_DOUBLES_EQUAL(102.126, aRect
.at(11).getMinX(), /*delta*/ 2.0);
5905 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(12).trim());
5906 CPPUNIT_ASSERT_DOUBLES_EQUAL(88.778, aRect
.at(12).getMinX(), /*delta*/ 2.0);
5907 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(13).trim());
5908 CPPUNIT_ASSERT_DOUBLES_EQUAL(97.754, aRect
.at(13).getMinX(), /*delta*/ 2.0);
5909 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(14).trim());
5910 CPPUNIT_ASSERT_DOUBLES_EQUAL(106.830, aRect
.at(14).getMinX(), /*delta*/ 2.0);
5911 CPPUNIT_ASSERT_EQUAL(u
"2 em variable size"_ustr
, aText
.at(15).trim());
5912 CPPUNIT_ASSERT_DOUBLES_EQUAL(115.906, aRect
.at(15).getMinX(), /*delta*/ 2.0);
5915 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf163913LeftRightMarginEm
)
5917 saveAsPDF(u
"tdf163913.fodt");
5919 auto pPdfDocument
= parsePDFExport();
5920 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5922 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
5923 CPPUNIT_ASSERT(pPdfPage
);
5924 auto pTextPage
= pPdfPage
->getTextPage();
5925 CPPUNIT_ASSERT(pTextPage
);
5927 int nPageObjectCount
= pPdfPage
->getObjectCount();
5929 CPPUNIT_ASSERT_EQUAL(9, nPageObjectCount
);
5931 std::vector
<OUString
> aText
;
5932 std::vector
<basegfx::B2DRectangle
> aRect
;
5934 for (int i
= 0; i
< nPageObjectCount
; ++i
)
5936 auto pPageObject
= pPdfPage
->getObject(i
);
5937 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
5938 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
5940 aText
.push_back(pPageObject
->getText(pTextPage
));
5941 aRect
.push_back(pPageObject
->getBounds());
5945 CPPUNIT_ASSERT_EQUAL(size_t(9), aText
.size());
5947 // Lines from the Writer portion
5948 CPPUNIT_ASSERT_EQUAL(u
"AAAAAAAAAAA"_ustr
, aText
.at(0).trim());
5949 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.800, aRect
.at(0).getMinX(), /*delta*/ 2.0);
5950 CPPUNIT_ASSERT_EQUAL(u
"AAAA"_ustr
, aText
.at(1).trim());
5951 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.800, aRect
.at(1).getMinX(), /*delta*/ 2.0);
5952 CPPUNIT_ASSERT_EQUAL(u
"AAAAAAAA"_ustr
, aText
.at(2).trim());
5953 CPPUNIT_ASSERT_DOUBLES_EQUAL(117.400, aRect
.at(2).getMinX(), /*delta*/ 2.0);
5954 CPPUNIT_ASSERT_EQUAL(u
"AAAAAAA"_ustr
, aText
.at(3).trim());
5955 CPPUNIT_ASSERT_DOUBLES_EQUAL(117.400, aRect
.at(3).getMinX(), /*delta*/ 2.0);
5957 CPPUNIT_ASSERT_EQUAL(u
""_ustr
, aText
.at(4).trim());
5958 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.800, aRect
.at(4).getMinX(), /*delta*/ 2.0);
5960 // Lines from the Edit Engine portion
5961 CPPUNIT_ASSERT_EQUAL(u
"AAAAAAAAAAA"_ustr
, aText
.at(5).trim());
5962 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.800, aRect
.at(5).getMinX(), /*delta*/ 2.0);
5963 CPPUNIT_ASSERT_EQUAL(u
"AAAA"_ustr
, aText
.at(6).trim());
5964 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.800, aRect
.at(6).getMinX(), /*delta*/ 2.0);
5965 CPPUNIT_ASSERT_EQUAL(u
"AAAAAAAA"_ustr
, aText
.at(7).trim());
5966 CPPUNIT_ASSERT_DOUBLES_EQUAL(123.750, aRect
.at(7).getMinX(), /*delta*/ 2.0);
5967 CPPUNIT_ASSERT_EQUAL(u
"AAAAAAA"_ustr
, aText
.at(8).trim());
5968 CPPUNIT_ASSERT_DOUBLES_EQUAL(123.750, aRect
.at(8).getMinX(), /*delta*/ 2.0);
5971 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testFormRoundtrip
)
5973 // Loads and saves a PDF with filled forms. This checks the forms survive the round-trip.
5975 // We need to enable PDFium import (and make sure to disable after the test)
5976 bool bResetEnvVar
= false;
5977 if (getenv("LO_IMPORT_USE_PDFIUM") == nullptr)
5979 bResetEnvVar
= true;
5980 osl_setEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
, u
"1"_ustr
.pData
);
5982 comphelper::ScopeGuard
aPDFiumEnvVarGuard([&]() {
5984 osl_clearEnvironment(u
"LO_IMPORT_USE_PDFIUM"_ustr
.pData
);
5987 // Need to properly set the PDF export options
5988 aMediaDescriptor
["FilterName"] <<= OUString("draw_pdf_Export");
5989 uno::Sequence
<beans::PropertyValue
> aFilterData(
5990 comphelper::InitPropertySequence({ { "UseTaggedPDF", uno::Any(true) } }));
5991 aMediaDescriptor
["FilterData"] <<= aFilterData
;
5993 saveAsPDF(u
"FilledUpForm.pdf");
5995 // Parse the round-tripped document with PDFium
5996 auto pPdfDocument
= parsePDFExport();
5998 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
5999 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPage
= pPdfDocument
->openPage(0);
6000 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPage
->getObject(1);
6002 // 5 annotations means 5 form fields
6003 CPPUNIT_ASSERT_EQUAL(5, pPage
->getAnnotationCount());
6007 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(0);
6008 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::CheckBox
,
6009 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
6013 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(1);
6014 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::ComboBox
,
6015 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
6019 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(2);
6020 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::TextField
,
6021 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
6025 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(3);
6026 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::TextField
,
6027 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
6031 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPage
->getAnnotation(4);
6032 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFFormFieldType::TextField
,
6033 pAnnotation
->getFormFieldType(pPdfDocument
.get()));
6037 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf162750SmallCapsLigature
)
6039 saveAsPDF(u
"tdf162750.fodt");
6041 auto pPdfDocument
= parsePDFExport();
6042 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
6044 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
6045 CPPUNIT_ASSERT(pPdfPage
);
6046 auto pTextPage
= pPdfPage
->getTextPage();
6047 CPPUNIT_ASSERT(pTextPage
);
6049 int nPageObjectCount
= pPdfPage
->getObjectCount();
6051 CPPUNIT_ASSERT_EQUAL(3, nPageObjectCount
);
6053 std::vector
<OUString
> aText
;
6054 for (int i
= 0; i
< nPageObjectCount
; ++i
)
6056 auto pPageObject
= pPdfPage
->getObject(i
);
6057 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
6058 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
6060 aText
.push_back(pPageObject
->getText(pTextPage
));
6064 CPPUNIT_ASSERT_EQUAL(size_t(3), aText
.size());
6065 CPPUNIT_ASSERT_EQUAL(u
"ffi"_ustr
, aText
.at(0).trim());
6067 // Without the fix, this will be "ffi"
6068 CPPUNIT_ASSERT_EQUAL(u
"f"_ustr
, aText
.at(1).trim());
6070 CPPUNIT_ASSERT_EQUAL(u
"FI"_ustr
, aText
.at(2).trim());
6073 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testTdf164106SplitReorderedClusters
)
6075 saveAsPDF(u
"tdf164106.fodt");
6077 auto pPdfDocument
= parsePDFExport();
6078 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
6080 auto pPdfPage
= pPdfDocument
->openPage(/*nIndex*/ 0);
6081 CPPUNIT_ASSERT(pPdfPage
);
6082 auto pTextPage
= pPdfPage
->getTextPage();
6083 CPPUNIT_ASSERT(pTextPage
);
6085 int nPageObjectCount
= pPdfPage
->getObjectCount();
6087 CPPUNIT_ASSERT_EQUAL(14, nPageObjectCount
);
6089 std::vector
<OUString
> aText
;
6090 std::vector
<basegfx::B2DRectangle
> aRect
;
6092 for (int i
= 0; i
< nPageObjectCount
; ++i
)
6094 auto pPageObject
= pPdfPage
->getObject(i
);
6095 CPPUNIT_ASSERT_MESSAGE("no object", pPageObject
!= nullptr);
6096 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
6098 aText
.push_back(pPageObject
->getText(pTextPage
));
6099 aRect
.push_back(pPageObject
->getBounds());
6103 CPPUNIT_ASSERT_EQUAL(size_t(14), aText
.size());
6105 auto fnCompareIndices
= [&](size_t nSplit
, size_t nCombined
) {
6106 CPPUNIT_ASSERT_EQUAL(aText
.at(nSplit
).trim(), aText
.at(nCombined
).trim());
6107 CPPUNIT_ASSERT_DOUBLES_EQUAL(aRect
.at(nSplit
).getMinX(), aRect
.at(nCombined
).getMinX(),
6109 CPPUNIT_ASSERT_DOUBLES_EQUAL(aRect
.at(nSplit
).getMaxX(), aRect
.at(nCombined
).getMaxX(),
6113 fnCompareIndices(0, 7);
6114 fnCompareIndices(1, 8);
6115 fnCompareIndices(2, 9);
6116 fnCompareIndices(3, 10);
6117 fnCompareIndices(4, 11);
6118 fnCompareIndices(5, 12);
6119 fnCompareIndices(6, 13);
6122 CPPUNIT_TEST_FIXTURE(PdfExportTest2
, testPDFAttachmentsWithEncryptedFile
)
6124 // Encrypt the document and use the hybrid mode.
6125 // The original ODF document will be saved to the PDF as an attachment.
6127 aMediaDescriptor
["FilterName"] <<= OUString("writer_pdf_Export");
6128 uno::Sequence
<beans::PropertyValue
> aFilterData
6129 = { comphelper::makePropertyValue("IsAddStream", true),
6130 comphelper::makePropertyValue("EncryptFile", true),
6131 comphelper::makePropertyValue("DocumentOpenPassword", OUString("secret")) };
6132 aMediaDescriptor
["FilterData"] <<= aFilterData
;
6134 saveAsPDF(u
"SimpleTestDocument.fodt");
6136 // Parse the round-tripped document with PDFium
6137 auto pPdfDocument
= parsePDFExport("secret"_ostr
);
6140 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
6142 // Should have 1 attachment
6143 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getAttachmentCount());
6145 // Get the attachment
6146 auto pAttachment
= pPdfDocument
->getAttachment(0);
6147 CPPUNIT_ASSERT(pAttachment
);
6149 // Check the filename of the attachment
6150 CPPUNIT_ASSERT_EQUAL(u
"Original.odt"_ustr
, pAttachment
->getName());
6152 // Write the attachment to the buffer
6153 std::vector
<sal_uInt8
> aBuffer
;
6154 CPPUNIT_ASSERT(pAttachment
->getFile(aBuffer
));
6155 CPPUNIT_ASSERT_GREATER(size_t(0), aBuffer
.size());
6157 // Create a temp file and store the content of the attachment
6158 utl::TempFileNamed aTempFile
;
6159 aTempFile
.EnableKillingFile();
6161 SvFileStream
aOutputStream(aTempFile
.GetURL(), StreamMode::WRITE
| StreamMode::TRUNC
);
6162 aOutputStream
.WriteBytes(aBuffer
.data(), aBuffer
.size());
6165 // Load the attached document from the temp file
6166 UnoApiTest::loadFromURL(aTempFile
.GetURL());
6168 // Check the content - first paragraph
6169 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
6170 CPPUNIT_ASSERT(xTextDocument
.is());
6171 uno::Reference
<container::XEnumerationAccess
> xParagraphEnumAccess(xTextDocument
->getText(),
6173 CPPUNIT_ASSERT(xParagraphEnumAccess
.is());
6174 uno::Reference
<container::XEnumeration
> xParagraphEnum
6175 = xParagraphEnumAccess
->createEnumeration();
6176 uno::Reference
<text::XTextContent
> const xElement(xParagraphEnum
->nextElement(),
6178 CPPUNIT_ASSERT(xElement
.is());
6179 uno::Reference
<text::XTextRange
> const xParagraph(xElement
, uno::UNO_QUERY
);
6180 CPPUNIT_ASSERT(xParagraph
.is());
6182 CPPUNIT_ASSERT_EQUAL(u
"This is a test document."_ustr
, xParagraph
->getString());
6185 } // end anonymous namespace
6187 CPPUNIT_PLUGIN_IMPLEMENT();
6189 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */