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>
17 #include <com/sun/star/view/XPrintable.hpp>
19 #include <comphelper/propertysequence.hxx>
20 #include <test/unoapi_test.hxx>
21 #include <unotools/mediadescriptor.hxx>
22 #include <unotools/tempfile.hxx>
23 #include <vcl/filter/pdfdocument.hxx>
24 #include <tools/zcodec.hxx>
25 #include <o3tl/string_view.hxx>
26 #include <officecfg/Office/Common.hxx>
28 #include <vcl/filter/PDFiumLibrary.hxx>
30 using namespace ::com::sun::star
;
34 /// Tests the PDF export filter.
35 class PdfExportTest
: public UnoApiTest
38 utl::MediaDescriptor aMediaDescriptor
;
42 : UnoApiTest(u
"/vcl/qa/cppunit/pdfexport/data/"_ustr
)
46 void saveAsPDF(std::u16string_view rFile
);
47 void load(std::u16string_view rFile
, vcl::filter::PDFDocument
& rDocument
);
50 void PdfExportTest::saveAsPDF(std::u16string_view rFile
)
52 // Import the bugdoc and export as PDF.
54 aMediaDescriptor
[u
"FilterName"_ustr
] <<= u
"writer_pdf_Export"_ustr
;
55 saveWithParams(aMediaDescriptor
.getAsConstPropertyValueList());
58 void PdfExportTest::load(std::u16string_view rFile
, vcl::filter::PDFDocument
& rDocument
)
62 // Parse the export result.
63 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
64 CPPUNIT_ASSERT(rDocument
.Read(aStream
));
67 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testPopupRectangleSize
)
69 // Enable Comment as PDF annotations
70 uno::Sequence
<beans::PropertyValue
> aFilterData(
71 comphelper::InitPropertySequence({ { "ExportNotes", uno::Any(true) } }));
72 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
73 saveAsPDF(u
"tdf162955_comment.odp");
75 // Parse the export result with pdfium.
76 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
77 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(0);
78 CPPUNIT_ASSERT(pPdfPage
);
82 auto pAnnotation
= pPdfPage
->getAnnotation(1);
83 CPPUNIT_ASSERT(pAnnotation
);
84 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Popup
, pAnnotation
->getSubType());
85 CPPUNIT_ASSERT(!pAnnotation
->getRectangle().isEmpty());
86 double nWidth
= pAnnotation
->getRectangle().getWidth();
87 double nHeight
= pAnnotation
->getRectangle().getHeight();
88 CPPUNIT_ASSERT(nWidth
> 0);
89 CPPUNIT_ASSERT(nHeight
> 0);
93 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testCommentAnnotation
)
95 // Enable PDF/UA and Comment as PDF annotations
96 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
97 { { "PDFUACompliance", uno::Any(true) }, { "ExportNotes", uno::Any(true) } }));
98 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
100 vcl::filter::PDFDocument aDocument
;
101 load(u
"tdf162359.odt", aDocument
);
103 // The document has one page.
104 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
105 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
107 vcl::filter::PDFObjectElement
* pAnnot(nullptr);
108 for (const auto& aElement
: aDocument
.GetElements())
110 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
113 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
114 if (pType
&& pType
->GetValue() == "StructElem")
116 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
117 if (pS
&& pS
->GetValue() == "Annot")
123 CPPUNIT_ASSERT(pAnnot
);
124 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("K"_ostr
));
125 CPPUNIT_ASSERT(pKids
);
126 auto pObj
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(0));
127 CPPUNIT_ASSERT(pObj
);
128 auto pOType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObj
->LookupElement("Type"_ostr
));
129 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
131 // Parse the export result with pdfium.
132 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
133 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
134 CPPUNIT_ASSERT(pPdfPage
);
136 // The page has two annotation.
137 CPPUNIT_ASSERT_EQUAL(2, pPdfPage
->getAnnotationCount());
140 auto pAnnotation
= pPdfPage
->getAnnotation(0);
141 CPPUNIT_ASSERT(pAnnotation
);
142 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Text
, pAnnotation
->getSubType());
143 CPPUNIT_ASSERT(pAnnotation
->hasKey("StructParent"_ostr
));
148 auto pAnnotation
= pPdfPage
->getAnnotation(1);
149 CPPUNIT_ASSERT(pAnnotation
);
150 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Popup
, pAnnotation
->getSubType());
151 CPPUNIT_ASSERT(!pAnnotation
->getRectangle().isEmpty());
155 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testFigurePlacement
)
157 vcl::filter::PDFDocument aDocument
;
158 load(u
"tdf159900_figurePlacement.odt", aDocument
);
160 // The document has one page.
161 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
162 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
164 for (const auto& aElement
: aDocument
.GetElements())
166 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
169 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
170 if (pType
&& pType
->GetValue() == "StructElem")
172 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
173 if (pS
&& pS
->GetValue() == "Figure")
176 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pObject
->Lookup("A"_ostr
));
177 CPPUNIT_ASSERT(pAttrDict
);
178 auto pOwner
= dynamic_cast<vcl::filter::PDFNameElement
*>(
179 pAttrDict
->LookupElement("O"_ostr
));
180 CPPUNIT_ASSERT(pOwner
);
181 if (pOwner
->GetValue() == "Layout")
183 auto pPlacement
= dynamic_cast<vcl::filter::PDFNameElement
*>(
184 pAttrDict
->LookupElement("Placement"_ostr
));
185 CPPUNIT_ASSERT(pPlacement
);
187 // Without the fix in place, this test would have failed with
190 CPPUNIT_ASSERT_EQUAL("Inline"_ostr
, pPlacement
->GetValue());
197 /// Tests that a pdf image is roundtripped back to PDF as a vector format.
198 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf106059
)
200 // Explicitly enable the usage of the reference XObject markup.
201 uno::Sequence
<beans::PropertyValue
> aFilterData(
202 comphelper::InitPropertySequence({ { "UseReferenceXObject", uno::Any(true) } }));
203 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
205 vcl::filter::PDFDocument aDocument
;
206 load(u
"tdf106059.odt", aDocument
);
208 // Assert that the XObject in the page resources dictionary is a reference XObject.
209 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
210 // The document has one page.
211 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
212 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
213 CPPUNIT_ASSERT(pResources
);
215 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
216 CPPUNIT_ASSERT(pXObjects
);
217 // The page has one image.
218 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
219 vcl::filter::PDFObjectElement
* pReferenceXObject
220 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
221 CPPUNIT_ASSERT(pReferenceXObject
);
222 // The image is a reference XObject.
223 // This dictionary key was missing, so the XObject wasn't a reference one.
224 CPPUNIT_ASSERT(pReferenceXObject
->Lookup("Ref"_ostr
));
227 /// Tests export of PDF images without reference XObjects.
228 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf106693
)
230 vcl::filter::PDFDocument aDocument
;
231 load(u
"tdf106693.odt", aDocument
);
233 // Assert that the XObject in the page resources dictionary is a form XObject.
234 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
235 // The document has one page.
236 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
237 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
238 CPPUNIT_ASSERT(pResources
);
240 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
241 CPPUNIT_ASSERT(pXObjects
);
242 // The page has one image.
243 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
244 vcl::filter::PDFObjectElement
* pXObject
245 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
246 CPPUNIT_ASSERT(pXObject
);
247 // The image is a form XObject.
248 auto pSubtype
= dynamic_cast<vcl::filter::PDFNameElement
*>(pXObject
->Lookup("Subtype"_ostr
));
249 CPPUNIT_ASSERT(pSubtype
);
250 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pSubtype
->GetValue());
251 // This failed: UseReferenceXObject was ignored and Ref was always created.
252 CPPUNIT_ASSERT(!pXObject
->Lookup("Ref"_ostr
));
254 // Assert that the form object refers to an inner form object, not a
257 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject
->Lookup("Resources"_ostr
));
258 CPPUNIT_ASSERT(pInnerResources
);
259 auto pInnerXObjects
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
260 pInnerResources
->LookupElement("XObject"_ostr
));
261 CPPUNIT_ASSERT(pInnerXObjects
);
262 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pInnerXObjects
->GetItems().size());
263 vcl::filter::PDFObjectElement
* pInnerXObject
264 = pInnerXObjects
->LookupObject(pInnerXObjects
->GetItems().begin()->first
);
265 CPPUNIT_ASSERT(pInnerXObject
);
267 = dynamic_cast<vcl::filter::PDFNameElement
*>(pInnerXObject
->Lookup("Subtype"_ostr
));
268 CPPUNIT_ASSERT(pInnerSubtype
);
269 // This failed: this was Image (bitmap), not Form (vector).
270 CPPUNIT_ASSERT_EQUAL("Form"_ostr
, pInnerSubtype
->GetValue());
273 /// Tests that text highlight from Impress is not lost.
274 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf105461
)
276 // Import the bugdoc and export as PDF.
277 saveAsPDF(u
"tdf105461.odp");
279 // Parse the export result with pdfium.
280 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
282 // The document has one page.
283 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
284 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
285 CPPUNIT_ASSERT(pPdfPage
);
287 // Make sure there is a filled rectangle inside.
288 int nPageObjectCount
= pPdfPage
->getObjectCount();
289 int nYellowPathCount
= 0;
290 for (int i
= 0; i
< nPageObjectCount
; ++i
)
292 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPdfPageObject
= pPdfPage
->getObject(i
);
293 if (pPdfPageObject
->getType() != vcl::pdf::PDFPageObjectType::Path
)
296 if (pPdfPageObject
->getFillColor() == COL_YELLOW
)
300 // This was 0, the page contained no yellow paths.
301 CPPUNIT_ASSERT_EQUAL(1, nYellowPathCount
);
304 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf107868
)
306 // No need to run it on Windows, since it would use GDI printing, and not trigger PDF export
307 // which is the intent of the test.
308 // FIXME: Why does this fail on macOS?
309 #if !defined MACOSX && !defined _WIN32
311 // Import the bugdoc and print to PDF.
312 loadFromFile(u
"tdf107868.odt");
313 uno::Reference
<view::XPrintable
> xPrintable(mxComponent
, uno::UNO_QUERY
);
314 CPPUNIT_ASSERT(xPrintable
.is());
315 uno::Sequence
<beans::PropertyValue
> aOptions(comphelper::InitPropertySequence(
316 { { "FileName", uno::Any(maTempFile
.GetURL()) }, { "Wait", uno::Any(true) } }));
317 xPrintable
->print(aOptions
);
319 // Parse the export result with pdfium.
320 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
322 // Printing to PDF failed in a non-interesting way, e.g. CUPS is not
323 // running, there is no printer defined, etc.
326 // The document has one page.
327 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
328 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
329 CPPUNIT_ASSERT(pPdfPage
);
331 // Make sure there is no filled rectangle inside.
332 int nPageObjectCount
= pPdfPage
->getObjectCount();
333 int nWhitePathCount
= 0;
334 for (int i
= 0; i
< nPageObjectCount
; ++i
)
336 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPdfPageObject
= pPdfPage
->getObject(i
);
337 if (pPdfPageObject
->getType() != vcl::pdf::PDFPageObjectType::Path
)
340 if (pPdfPageObject
->getFillColor() == COL_WHITE
)
344 // This was 4, the page contained 4 white paths at problematic positions.
345 CPPUNIT_ASSERT_EQUAL(0, nWhitePathCount
);
349 /// Tests that embedded video from Impress is not exported as a linked one.
350 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf105093
)
352 vcl::filter::PDFDocument aDocument
;
353 load(u
"tdf105093.odp", aDocument
);
355 // The document has one page.
356 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
357 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
359 // Get page annotations.
360 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
361 CPPUNIT_ASSERT(pAnnots
);
362 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots
->GetElements().size());
364 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pAnnots
->GetElements()[0]);
365 CPPUNIT_ASSERT(pAnnotReference
);
366 vcl::filter::PDFObjectElement
* pAnnot
= pAnnotReference
->LookupObject();
367 CPPUNIT_ASSERT(pAnnot
);
368 CPPUNIT_ASSERT_EQUAL(
370 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
))->GetValue());
372 // Get the Action -> Rendition -> MediaClip -> FileSpec.
373 auto pAction
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAnnot
->Lookup("A"_ostr
));
374 CPPUNIT_ASSERT(pAction
);
376 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAction
->LookupElement("R"_ostr
));
377 CPPUNIT_ASSERT(pRendition
);
379 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pRendition
->LookupElement("C"_ostr
));
380 CPPUNIT_ASSERT(pMediaClip
);
382 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pMediaClip
->LookupElement("D"_ostr
));
383 CPPUNIT_ASSERT(pFileSpec
);
384 // Make sure the filespec refers to an embedded file.
385 // This key was missing, the embedded video was handled as a linked one.
386 CPPUNIT_ASSERT(pFileSpec
->LookupElement("EF"_ostr
));
389 /// Tests export of non-PDF images.
390 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf106206
)
392 // Import the bugdoc and export as PDF.
393 vcl::filter::PDFDocument aDocument
;
394 load(u
"tdf106206.odt", aDocument
);
396 // The document has one page.
397 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
398 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
400 // The page has a stream.
401 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
402 CPPUNIT_ASSERT(pContents
);
403 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
404 CPPUNIT_ASSERT(pStream
);
405 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
407 SvMemoryStream aUncompressed
;
409 aZCodec
.BeginCompression();
410 rObjectStream
.Seek(0);
411 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
412 CPPUNIT_ASSERT(aZCodec
.EndCompression());
414 // Make sure there is an image reference there.
415 OString
aImage("/Im"_ostr
);
416 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
417 const char* pEnd
= pStart
+ aUncompressed
.GetSize();
418 auto it
= std::search(pStart
, pEnd
, aImage
.getStr(), aImage
.getStr() + aImage
.getLength());
419 CPPUNIT_ASSERT(it
!= pEnd
);
421 // And also that it's not an invalid one.
422 OString
aInvalidImage("/Im0"_ostr
);
423 it
= std::search(pStart
, pEnd
, aInvalidImage
.getStr(),
424 aInvalidImage
.getStr() + aInvalidImage
.getLength());
425 // This failed, object #0 was referenced.
426 CPPUNIT_ASSERT(bool(it
== pEnd
));
429 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf127217
)
431 // Import the bugdoc and export as PDF.
432 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
433 { "ExportFormFields", uno::Any(true) },
435 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
436 saveAsPDF(u
"tdf127217.odt");
438 // Parse the export result with pdfium.
439 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
441 // The document has one page.
442 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
443 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
444 CPPUNIT_ASSERT(pPdfPage
);
446 // The page has one annotation.
447 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getAnnotationCount());
448 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnot
= pPdfPage
->getAnnotation(0);
450 // Without the fix in place, this test would have failed here
451 CPPUNIT_ASSERT(!pAnnot
->hasKey("DA"_ostr
));
454 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf142741
)
456 // Import the doc and export as PDF.
457 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
458 { "ExportFormFields", uno::Any(true) },
460 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
461 saveAsPDF(u
"tdf142741.odt");
463 // Parse the export result with pdfium.
464 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
466 CPPUNIT_ASSERT_EQUAL(2,
467 pPdfDocument
->getPageCount()); // To ensure that exported pdf has 2 pages
469 for (int pageNum
= 0; pageNum
< 2; ++pageNum
)
471 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(pageNum
);
472 CPPUNIT_ASSERT(pPdfPage
);
474 CPPUNIT_ASSERT_EQUAL(1,
475 pPdfPage
->getAnnotationCount()); // Expect only one annotation per page
476 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnotation
= pPdfPage
->getAnnotation(0);
477 CPPUNIT_ASSERT(pAnnotation
);
479 // Check if annotation is a link and has the expected content, here "1" (both in first page content and footnote of second page)
480 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Link
, pAnnotation
->getSubType());
481 OUString content
= pAnnotation
->getString(vcl::pdf::constDictionaryKeyContents
);
482 CPPUNIT_ASSERT_EQUAL(u
"1"_ustr
, content
);
486 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf109143
)
488 // Import the bugdoc and export as PDF.
489 vcl::filter::PDFDocument aDocument
;
490 load(u
"tdf109143.odt", aDocument
);
492 // The document has one page.
493 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
494 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
496 // Get access to the only image on the only page.
497 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
498 CPPUNIT_ASSERT(pResources
);
500 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
501 CPPUNIT_ASSERT(pXObjects
);
502 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
503 vcl::filter::PDFObjectElement
* pXObject
504 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
505 CPPUNIT_ASSERT(pXObject
);
507 // Make sure it's re-compressed.
508 auto pLength
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pXObject
->Lookup("Length"_ostr
));
509 CPPUNIT_ASSERT(pLength
);
510 int nLength
= pLength
->GetValue();
511 // This failed: cropped TIFF-in-JPEG wasn't re-compressed, so crop was
512 // lost. Size was 59416, now is 11827.
513 CPPUNIT_ASSERT(nLength
< 50000);
516 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf106972
)
518 // Import the bugdoc and export as PDF.
519 vcl::filter::PDFDocument aDocument
;
520 load(u
"tdf106972.odt", aDocument
);
522 // Get access to the only form object on the only page.
523 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
524 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
525 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
526 CPPUNIT_ASSERT(pResources
);
528 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
529 CPPUNIT_ASSERT(pXObjects
);
530 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
531 vcl::filter::PDFObjectElement
* pXObject
532 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
533 CPPUNIT_ASSERT(pXObject
);
535 // Get access to the only image inside the form object.
537 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject
->Lookup("Resources"_ostr
));
538 CPPUNIT_ASSERT(pFormResources
);
539 auto pImages
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
540 pFormResources
->LookupElement("XObject"_ostr
));
541 CPPUNIT_ASSERT(pImages
);
542 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pImages
->GetItems().size());
543 vcl::filter::PDFObjectElement
* pImage
544 = pImages
->LookupObject(pImages
->GetItems().begin()->first
);
545 CPPUNIT_ASSERT(pImage
);
547 // Assert resources of the image.
549 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pImage
->Lookup("Resources"_ostr
));
550 CPPUNIT_ASSERT(pImageResources
);
551 // This failed: the PDF image had no Font resource.
552 CPPUNIT_ASSERT(pImageResources
->LookupElement("Font"_ostr
));
555 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf106972Pdf17
)
557 // Import the bugdoc and export as PDF.
558 vcl::filter::PDFDocument aDocument
;
559 load(u
"tdf106972-pdf17.odt", aDocument
);
561 // Get access to the only image on the only page.
562 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
563 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
564 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
565 CPPUNIT_ASSERT(pResources
);
567 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
568 CPPUNIT_ASSERT(pXObjects
);
569 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
570 vcl::filter::PDFObjectElement
* pXObject
571 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
572 CPPUNIT_ASSERT(pXObject
);
574 // Assert that we now attempt to preserve the original PDF data, even if
575 // the original input was PDF >= 1.4.
576 CPPUNIT_ASSERT(pXObject
->Lookup("Resources"_ostr
));
579 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testSofthyphenPos
)
581 // No need to run it on Windows, since it would use GDI printing, and not trigger PDF export
582 // which is the intent of the test.
583 // FIXME: Why does this fail on macOS?
584 #if !defined MACOSX && !defined _WIN32
586 // Import the bugdoc and print to PDF.
587 loadFromFile(u
"softhyphen_pdf.odt");
588 uno::Reference
<view::XPrintable
> xPrintable(mxComponent
, uno::UNO_QUERY
);
589 CPPUNIT_ASSERT(xPrintable
.is());
590 uno::Sequence
<beans::PropertyValue
> aOptions(comphelper::InitPropertySequence(
591 { { "FileName", uno::Any(maTempFile
.GetURL()) }, { "Wait", uno::Any(true) } }));
592 xPrintable
->print(aOptions
);
594 // Parse the export result with pdfium.
595 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
597 // Printing to PDF failed in a non-interesting way, e.g. CUPS is not
598 // running, there is no printer defined, etc.
601 // The document has one page.
602 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
603 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
604 CPPUNIT_ASSERT(pPdfPage
);
606 // tdf#96892 incorrect fractional part of font size caused soft-hyphen to
607 // be positioned inside preceding text (incorrect = 11.1, correct = 11.05)
609 // there are 3 texts currently, for line 1, soft-hyphen, line 2
610 bool haveText(false);
612 int nPageObjectCount
= pPdfPage
->getObjectCount();
613 for (int i
= 0; i
< nPageObjectCount
; ++i
)
615 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPdfPageObject
= pPdfPage
->getObject(i
);
616 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFPageObjectType::Text
, pPdfPageObject
->getType());
618 double const size
= pPdfPageObject
->getFontSize();
619 CPPUNIT_ASSERT_DOUBLES_EQUAL(11.05, size
, 1E-06);
622 CPPUNIT_ASSERT(haveText
);
626 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf107013
)
628 vcl::filter::PDFDocument aDocument
;
629 load(u
"tdf107013.odt", aDocument
);
631 // Get access to the only image on the only page.
632 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
633 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
634 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
635 CPPUNIT_ASSERT(pResources
);
637 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
638 CPPUNIT_ASSERT(pXObjects
);
639 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
640 vcl::filter::PDFObjectElement
* pXObject
641 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
642 // This failed, the reference to the image was created, but not the image.
643 CPPUNIT_ASSERT(pXObject
);
646 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf107018
)
648 vcl::filter::PDFDocument aDocument
;
649 load(u
"tdf107018.odt", aDocument
);
651 // Get access to the only image on the only page.
652 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
653 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
654 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
655 CPPUNIT_ASSERT(pResources
);
657 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
658 CPPUNIT_ASSERT(pXObjects
);
659 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
660 vcl::filter::PDFObjectElement
* pXObject
661 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
662 CPPUNIT_ASSERT(pXObject
);
664 // Get access to the form object inside the image.
665 auto pXObjectResources
666 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject
->Lookup("Resources"_ostr
));
667 CPPUNIT_ASSERT(pXObjectResources
);
668 auto pXObjectForms
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
669 pXObjectResources
->LookupElement("XObject"_ostr
));
670 CPPUNIT_ASSERT(pXObjectForms
);
671 vcl::filter::PDFObjectElement
* pForm
672 = pXObjectForms
->LookupObject(pXObjectForms
->GetItems().begin()->first
);
673 CPPUNIT_ASSERT(pForm
);
675 // Get access to Resources -> Font -> F1 of the form.
677 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pForm
->Lookup("Resources"_ostr
));
678 CPPUNIT_ASSERT(pFormResources
);
679 auto pFonts
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
680 pFormResources
->LookupElement("Font"_ostr
));
681 CPPUNIT_ASSERT(pFonts
);
682 auto pF1Ref
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pFonts
->LookupElement("F1"_ostr
));
683 CPPUNIT_ASSERT(pF1Ref
);
684 vcl::filter::PDFObjectElement
* pF1
= pF1Ref
->LookupObject();
687 // Check that Foo -> Bar of the font is of type Pages.
688 auto pFontFoo
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pF1
->Lookup("Foo"_ostr
));
689 CPPUNIT_ASSERT(pFontFoo
);
691 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pFontFoo
->LookupElement("Bar"_ostr
));
692 CPPUNIT_ASSERT(pBar
);
693 vcl::filter::PDFObjectElement
* pObject
= pBar
->LookupObject();
694 CPPUNIT_ASSERT(pObject
);
695 auto pName
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
696 CPPUNIT_ASSERT(pName
);
697 // This was "XObject", reference in a nested dictionary wasn't updated when
698 // copying the page stream of a PDF image.
699 CPPUNIT_ASSERT_EQUAL("Pages"_ostr
, pName
->GetValue());
702 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf148706
)
704 // Import the bugdoc and export as PDF.
705 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
706 { "ExportFormFields", uno::Any(true) },
708 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
709 saveAsPDF(u
"tdf148706.odt");
711 // Parse the export result with pdfium.
712 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
714 // The document has one page.
715 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
716 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
717 CPPUNIT_ASSERT(pPdfPage
);
719 // The page has one annotation.
720 CPPUNIT_ASSERT_EQUAL(1, pPdfPage
->getAnnotationCount());
721 std::unique_ptr
<vcl::pdf::PDFiumAnnotation
> pAnnot
= pPdfPage
->getAnnotation(0);
723 CPPUNIT_ASSERT(pAnnot
->hasKey("V"_ostr
));
724 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFObjectType::String
, pAnnot
->getValueType("V"_ostr
));
725 OUString aV
= pAnnot
->getString("V"_ostr
);
727 // Without the fix in place, this test would have failed with
728 // - Expected: 1821.84
730 CPPUNIT_ASSERT_EQUAL(u
"1821.84"_ustr
, aV
);
732 CPPUNIT_ASSERT(pAnnot
->hasKey("DV"_ostr
));
733 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFObjectType::String
, pAnnot
->getValueType("DV"_ostr
));
734 OUString aDV
= pAnnot
->getString("DV"_ostr
);
736 CPPUNIT_ASSERT_EQUAL(u
"1821.84"_ustr
, aDV
);
739 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf107089
)
741 vcl::filter::PDFDocument aDocument
;
742 load(u
"tdf107089.odt", aDocument
);
744 // Get access to the only image on the only page.
745 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
746 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
747 vcl::filter::PDFObjectElement
* pResources
= aPages
[0]->LookupObject("Resources"_ostr
);
748 CPPUNIT_ASSERT(pResources
);
750 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pResources
->Lookup("XObject"_ostr
));
751 CPPUNIT_ASSERT(pXObjects
);
752 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pXObjects
->GetItems().size());
753 vcl::filter::PDFObjectElement
* pXObject
754 = pXObjects
->LookupObject(pXObjects
->GetItems().begin()->first
);
755 CPPUNIT_ASSERT(pXObject
);
757 // Get access to the form object inside the image.
758 auto pXObjectResources
759 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pXObject
->Lookup("Resources"_ostr
));
760 CPPUNIT_ASSERT(pXObjectResources
);
761 auto pXObjectForms
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(
762 pXObjectResources
->LookupElement("XObject"_ostr
));
763 CPPUNIT_ASSERT(pXObjectForms
);
764 vcl::filter::PDFObjectElement
* pForm
765 = pXObjectForms
->LookupObject(pXObjectForms
->GetItems().begin()->first
);
766 CPPUNIT_ASSERT(pForm
);
768 // Make sure 'Hello' is part of the form object's stream.
769 vcl::filter::PDFStreamElement
* pStream
= pForm
->GetStream();
770 CPPUNIT_ASSERT(pStream
);
771 SvMemoryStream aObjectStream
;
773 aZCodec
.BeginCompression();
774 pStream
->GetMemory().Seek(0);
775 aZCodec
.Decompress(pStream
->GetMemory(), aObjectStream
);
776 CPPUNIT_ASSERT(aZCodec
.EndCompression());
777 aObjectStream
.Seek(0);
778 OString
aHello("Hello"_ostr
);
779 auto pStart
= static_cast<const char*>(aObjectStream
.GetData());
780 const char* pEnd
= pStart
+ aObjectStream
.GetSize();
781 auto it
= std::search(pStart
, pEnd
, aHello
.getStr(), aHello
.getStr() + aHello
.getLength());
782 // This failed, 'Hello' was part only a mixed compressed/uncompressed stream, i.e. garbage.
783 CPPUNIT_ASSERT(it
!= pEnd
);
786 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf99680
)
788 vcl::filter::PDFDocument aDocument
;
789 load(u
"tdf99680.odt", aDocument
);
791 // The document has one page.
792 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
793 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
795 // The page 1 has a stream.
796 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
797 CPPUNIT_ASSERT(pContents
);
798 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
799 CPPUNIT_ASSERT(pStream
);
800 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
803 SvMemoryStream aUncompressed
;
805 aZCodec
.BeginCompression();
806 rObjectStream
.Seek(0);
807 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
808 CPPUNIT_ASSERT(aZCodec
.EndCompression());
810 // tdf#130150 See infos in task - short: tdf#99680 was not the
811 // correct fix, so empty clip regions are valid - allow again in tests
812 // Make sure there are no empty clipping regions.
813 // OString aEmptyRegion("0 0 m h W* n");
814 // auto it = std::search(pStart, pEnd, aEmptyRegion.getStr(), aEmptyRegion.getStr() + aEmptyRegion.getLength());
815 // CPPUNIT_ASSERT_EQUAL_MESSAGE("Empty clipping region detected!", it, pEnd);
817 // Count save graphic state (q) and restore (Q) operators
818 // and ensure their amount is equal
819 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
820 const char* pEnd
= pStart
+ aUncompressed
.GetSize();
821 size_t nSaveCount
= std::count(pStart
, pEnd
, 'q');
822 size_t nRestoreCount
= std::count(pStart
, pEnd
, 'Q');
823 CPPUNIT_ASSERT_EQUAL_MESSAGE("Save/restore graphic state operators count mismatch!", nSaveCount
,
827 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf99680_2
)
829 vcl::filter::PDFDocument aDocument
;
830 load(u
"tdf99680-2.odt", aDocument
);
832 // For each document page
833 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
834 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPages
.size());
835 for (size_t nPageNr
= 0; nPageNr
< aPages
.size(); nPageNr
++)
837 // Get page contents and stream.
838 vcl::filter::PDFObjectElement
* pContents
= aPages
[nPageNr
]->LookupObject("Contents"_ostr
);
839 CPPUNIT_ASSERT(pContents
);
840 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
841 CPPUNIT_ASSERT(pStream
);
842 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
844 // Uncompress the stream.
845 SvMemoryStream aUncompressed
;
847 aZCodec
.BeginCompression();
848 rObjectStream
.Seek(0);
849 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
850 CPPUNIT_ASSERT(aZCodec
.EndCompression());
852 // tdf#130150 See infos in task - short: tdf#99680 was not the
853 // correct fix, so empty clip regions are valid - allow again in tests
854 // Make sure there are no empty clipping regions.
855 // OString aEmptyRegion("0 0 m h W* n");
856 // auto it = std::search(pStart, pEnd, aEmptyRegion.getStr(), aEmptyRegion.getStr() + aEmptyRegion.getLength());
857 // CPPUNIT_ASSERT_EQUAL_MESSAGE("Empty clipping region detected!", it, pEnd);
859 // Count save graphic state (q) and restore (Q) operators
860 // and ensure their amount is equal
861 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
862 const char* pEnd
= pStart
+ aUncompressed
.GetSize();
863 size_t nSaveCount
= std::count(pStart
, pEnd
, 'q');
864 size_t nRestoreCount
= std::count(pStart
, pEnd
, 'Q');
865 CPPUNIT_ASSERT_EQUAL_MESSAGE("Save/restore graphic state operators count mismatch!",
866 nSaveCount
, nRestoreCount
);
870 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf108963
)
872 // Import the bugdoc and export as PDF.
873 saveAsPDF(u
"tdf108963.odp");
875 // Parse the export result with pdfium.
876 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
878 // The document has one page.
879 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
880 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
881 CPPUNIT_ASSERT(pPdfPage
);
883 // Test page size (28x15.75 cm, was 1/100th mm off, tdf#112690)
884 // bad: MediaBox[0 0 793.672440944882 446.428346456693]
885 // good: MediaBox[0 0 793.700787401575 446.456692913386]
886 const double aWidth
= pPdfPage
->getWidth();
887 CPPUNIT_ASSERT_DOUBLES_EQUAL(793.7, aWidth
, 0.01);
888 const double aHeight
= pPdfPage
->getHeight();
889 CPPUNIT_ASSERT_DOUBLES_EQUAL(446.46, aHeight
, 0.01);
891 // Make sure there is a filled rectangle inside.
892 int nPageObjectCount
= pPdfPage
->getObjectCount();
893 int nYellowPathCount
= 0;
894 for (int i
= 0; i
< nPageObjectCount
; ++i
)
896 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPdfPageObject
= pPdfPage
->getObject(i
);
897 if (pPdfPageObject
->getType() != vcl::pdf::PDFPageObjectType::Path
)
900 if (pPdfPageObject
->getFillColor() == COL_YELLOW
)
903 // The path described a yellow rectangle, but it was not rotated.
904 int nSegments
= pPdfPageObject
->getPathSegmentCount();
905 CPPUNIT_ASSERT_EQUAL(5, nSegments
);
906 std::unique_ptr
<vcl::pdf::PDFiumPathSegment
> pSegment
907 = pPdfPageObject
->getPathSegment(0);
908 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFSegmentType::Moveto
, pSegment
->getType());
909 basegfx::B2DPoint aPoint
= pSegment
->getPoint();
910 CPPUNIT_ASSERT_DOUBLES_EQUAL(245, aPoint
.getX(), 0.999);
911 CPPUNIT_ASSERT_DOUBLES_EQUAL(244, aPoint
.getY(), 0.999);
912 CPPUNIT_ASSERT(!pSegment
->isClosed());
914 pSegment
= pPdfPageObject
->getPathSegment(1);
915 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFSegmentType::Lineto
, pSegment
->getType());
916 aPoint
= pSegment
->getPoint();
917 CPPUNIT_ASSERT_DOUBLES_EQUAL(275, aPoint
.getX(), 0.999);
918 CPPUNIT_ASSERT_DOUBLES_EQUAL(267, aPoint
.getY(), 0.999);
919 CPPUNIT_ASSERT(!pSegment
->isClosed());
921 pSegment
= pPdfPageObject
->getPathSegment(2);
922 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFSegmentType::Lineto
, pSegment
->getType());
923 aPoint
= pSegment
->getPoint();
924 CPPUNIT_ASSERT_DOUBLES_EQUAL(287, aPoint
.getX(), 0.999);
925 CPPUNIT_ASSERT_DOUBLES_EQUAL(251, aPoint
.getY(), 0.999);
926 CPPUNIT_ASSERT(!pSegment
->isClosed());
928 pSegment
= pPdfPageObject
->getPathSegment(3);
929 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFSegmentType::Lineto
, pSegment
->getType());
930 aPoint
= pSegment
->getPoint();
931 CPPUNIT_ASSERT_DOUBLES_EQUAL(257, aPoint
.getX(), 0.999);
932 CPPUNIT_ASSERT_DOUBLES_EQUAL(228, aPoint
.getY(), 0.999);
933 CPPUNIT_ASSERT(!pSegment
->isClosed());
935 pSegment
= pPdfPageObject
->getPathSegment(4);
936 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFSegmentType::Lineto
, pSegment
->getType());
937 aPoint
= pSegment
->getPoint();
938 CPPUNIT_ASSERT_DOUBLES_EQUAL(245, aPoint
.getX(), 0.999);
939 CPPUNIT_ASSERT_DOUBLES_EQUAL(244, aPoint
.getY(), 0.999);
940 CPPUNIT_ASSERT(pSegment
->isClosed());
944 CPPUNIT_ASSERT_EQUAL(1, nYellowPathCount
);
947 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testAlternativeText
)
949 vcl::filter::PDFDocument aDocument
;
950 load(u
"alternativeText.fodp", aDocument
);
952 // The document has one page.
953 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
954 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
956 for (const auto& aElement
: aDocument
.GetElements())
958 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
961 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
962 if (pType
&& pType
->GetValue() == "StructElem")
964 auto pS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("S"_ostr
));
965 if (pS
&& pS
->GetValue() == "Figure")
967 CPPUNIT_ASSERT_EQUAL(u
"This is the text alternative - This is the description"_ustr
,
968 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(
969 *dynamic_cast<vcl::filter::PDFHexStringElement
*>(
970 pObject
->Lookup("Alt"_ostr
))));
975 // tdf#67866 check that Catalog contains Lang
976 auto* pCatalog
= aDocument
.GetCatalog();
977 CPPUNIT_ASSERT(pCatalog
);
978 auto* pCatalogDictionary
= pCatalog
->GetDictionary();
979 CPPUNIT_ASSERT(pCatalogDictionary
);
980 auto pLang
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
981 pCatalogDictionary
->LookupElement("Lang"_ostr
));
982 CPPUNIT_ASSERT(pLang
);
983 CPPUNIT_ASSERT_EQUAL("en-US"_ostr
, pLang
->GetValue());
986 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf105972
)
988 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
989 { "ExportFormFields", uno::Any(true) },
991 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
992 vcl::filter::PDFDocument aDocument
;
993 load(u
"tdf105972.fodt", aDocument
);
995 // The document has one page.
996 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
997 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
999 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
1000 CPPUNIT_ASSERT(pAnnots
);
1001 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pAnnots
->GetElements().size());
1003 sal_uInt32 nTextFieldCount
= 0;
1004 for (const auto& aElement
: aDocument
.GetElements())
1006 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1009 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("FT"_ostr
));
1010 if (pType
&& pType
->GetValue() == "Tx")
1015 = dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(pObject
->Lookup("T"_ostr
));
1017 auto pAA
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pObject
->Lookup("AA"_ostr
));
1018 CPPUNIT_ASSERT(pAA
);
1019 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pAA
->GetItems().size());
1021 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAA
->LookupElement("F"_ostr
));
1023 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pF
->GetItems().size());
1025 if (nTextFieldCount
== 1)
1027 CPPUNIT_ASSERT_EQUAL("CurrencyField"_ostr
, pT
->GetValue());
1029 auto pJS
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
1030 pF
->LookupElement("JS"_ostr
));
1031 CPPUNIT_ASSERT_EQUAL("AFNumber_Format\\(4, 0, 0, 0, \"\\\\u20ac\",true\\);"_ostr
,
1034 else if (nTextFieldCount
== 2)
1036 CPPUNIT_ASSERT_EQUAL("TimeField"_ostr
, pT
->GetValue());
1038 auto pJS
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
1039 pF
->LookupElement("JS"_ostr
));
1040 CPPUNIT_ASSERT_EQUAL("AFTime_FormatEx\\(\"h:MM:sstt\"\\);"_ostr
, pJS
->GetValue());
1044 CPPUNIT_ASSERT_EQUAL("DateField"_ostr
, pT
->GetValue());
1046 auto pJS
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(
1047 pF
->LookupElement("JS"_ostr
));
1048 CPPUNIT_ASSERT_EQUAL("AFDate_FormatEx\\(\"yy-mm-dd\"\\);"_ostr
, pJS
->GetValue());
1054 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf148442
)
1056 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
1057 { "ExportFormFields", uno::Any(true) },
1059 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1061 vcl::filter::PDFDocument aDocument
;
1062 load(u
"tdf148442.odt", aDocument
);
1064 // The document has one page.
1065 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1066 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1068 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
1069 CPPUNIT_ASSERT(pAnnots
);
1070 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), pAnnots
->GetElements().size());
1072 sal_uInt32 nBtnCount
= 0;
1073 for (const auto& aElement
: aDocument
.GetElements())
1075 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1078 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("FT"_ostr
));
1079 if (pType
&& pType
->GetValue() == "Btn")
1083 = dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(pObject
->Lookup("T"_ostr
));
1085 auto pAS
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("AS"_ostr
));
1086 CPPUNIT_ASSERT(pAS
);
1088 auto pAP
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pObject
->Lookup("AP"_ostr
));
1089 CPPUNIT_ASSERT(pAP
);
1091 = dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAP
->LookupElement("N"_ostr
));
1093 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pN
->GetItems().size());
1097 CPPUNIT_ASSERT_EQUAL("Checkbox1"_ostr
, pT
->GetValue());
1098 CPPUNIT_ASSERT_EQUAL("Yes"_ostr
, pAS
->GetValue());
1099 CPPUNIT_ASSERT(!pN
->GetItems().count("ref"_ostr
));
1100 CPPUNIT_ASSERT(pN
->GetItems().count("Yes"_ostr
));
1101 CPPUNIT_ASSERT(pN
->GetItems().count("Off"_ostr
));
1103 else if (nBtnCount
== 2)
1105 CPPUNIT_ASSERT_EQUAL("Checkbox2"_ostr
, pT
->GetValue());
1106 CPPUNIT_ASSERT_EQUAL("Yes"_ostr
, pAS
->GetValue());
1108 // Without the fix in place, this test would have failed here
1109 CPPUNIT_ASSERT(pN
->GetItems().count("ref"_ostr
));
1110 CPPUNIT_ASSERT(!pN
->GetItems().count("Yes"_ostr
));
1111 CPPUNIT_ASSERT(pN
->GetItems().count("Off"_ostr
));
1115 CPPUNIT_ASSERT_EQUAL("Checkbox3"_ostr
, pT
->GetValue());
1116 CPPUNIT_ASSERT_EQUAL("Off"_ostr
, pAS
->GetValue());
1117 CPPUNIT_ASSERT(pN
->GetItems().count("ref"_ostr
));
1118 CPPUNIT_ASSERT(!pN
->GetItems().count("Yes"_ostr
));
1120 // tdf#143612: Without the fix in place, this test would have failed here
1121 CPPUNIT_ASSERT(!pN
->GetItems().count("Off"_ostr
));
1122 CPPUNIT_ASSERT(pN
->GetItems().count("refOff"_ostr
));
1128 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf118244_radioButtonGroup
)
1130 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
1131 { "ExportFormFields", uno::Any(true) },
1133 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1135 vcl::filter::PDFDocument aDocument
;
1136 load(u
"tdf118244_radioButtonGroup.odt", aDocument
);
1138 // The document has one page.
1139 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
1140 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1142 // There are eight radio buttons.
1143 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
1144 CPPUNIT_ASSERT(pAnnots
);
1145 CPPUNIT_ASSERT_EQUAL_MESSAGE("# of radio buttons", static_cast<size_t>(8),
1146 pAnnots
->GetElements().size());
1148 sal_uInt32 nRadioGroups
= 0;
1149 for (const auto& aElement
: aDocument
.GetElements())
1151 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1154 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("FT"_ostr
));
1155 if (pType
&& pType
->GetValue() == "Btn")
1157 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObject
->Lookup("Kids"_ostr
));
1160 size_t expectedSize
= 2;
1162 if (nRadioGroups
== 3)
1164 CPPUNIT_ASSERT_EQUAL(expectedSize
, pKids
->GetElements().size());
1168 CPPUNIT_ASSERT_EQUAL_MESSAGE("# of radio groups", sal_uInt32(3), nRadioGroups
);
1171 /// Test writing ToUnicode CMAP for LTR ligatures.
1172 // This requires Carlito font, if it is missing the test will most likely
1174 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf115117_1
)
1177 vcl::filter::PDFDocument aDocument
;
1178 load(u
"tdf115117-1.odt", aDocument
);
1180 vcl::filter::PDFObjectElement
* pToUnicode
= nullptr;
1182 // Get access to ToUnicode of the first font
1183 for (const auto& aElement
: aDocument
.GetElements())
1185 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1188 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1189 if (pType
&& pType
->GetValue() == "Font")
1191 auto pToUnicodeRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1192 pObject
->Lookup("ToUnicode"_ostr
));
1193 CPPUNIT_ASSERT(pToUnicodeRef
);
1194 pToUnicode
= pToUnicodeRef
->LookupObject();
1199 CPPUNIT_ASSERT(pToUnicode
);
1200 auto pStream
= pToUnicode
->GetStream();
1201 CPPUNIT_ASSERT(pStream
);
1202 SvMemoryStream aObjectStream
;
1204 aZCodec
.BeginCompression();
1205 pStream
->GetMemory().Seek(0);
1206 aZCodec
.Decompress(pStream
->GetMemory(), aObjectStream
);
1207 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1208 aObjectStream
.Seek(0);
1209 // The first values, <01> <02> etc., are glyph ids, they might change order
1210 // if we changed how font subsets are created.
1211 // The second values, <00740069> etc., are Unicode code points in hex,
1212 // <00740069> is U+0074 and U+0069 i.e. "ti" which is a ligature in
1213 // Carlito/Calibri. This test is failing if any of the second values
1214 // changed which means we are not detecting ligatures and writing CMAP
1215 // entries for them correctly. If glyph order in the subset changes then
1216 // the order here will changes and the PDF has to be carefully inspected to
1217 // ensure that the new values are correct before updating the string below.
1218 OString
aCmap("9 beginbfchar\n"
1226 "<08> <006600660069>\n"
1227 "<09> <00660066006C>\n"
1229 auto pStart
= static_cast<const char*>(aObjectStream
.GetData());
1230 const char* pEnd
= pStart
+ aObjectStream
.GetSize();
1231 auto it
= std::search(pStart
, pEnd
, aCmap
.getStr(), aCmap
.getStr() + aCmap
.getLength());
1232 CPPUNIT_ASSERT(it
!= pEnd
);
1236 /// Test writing ToUnicode CMAP for RTL ligatures.
1237 // This requires DejaVu Sans font, if it is missing the test will most likely
1239 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf115117_2
)
1242 // See the comments in testTdf115117_1() for explanation.
1244 vcl::filter::PDFDocument aDocument
;
1245 load(u
"tdf115117-2.odt", aDocument
);
1247 vcl::filter::PDFObjectElement
* pToUnicode
= nullptr;
1249 for (const auto& aElement
: aDocument
.GetElements())
1251 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1254 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1255 if (pType
&& pType
->GetValue() == "Font")
1257 auto pToUnicodeRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1258 pObject
->Lookup("ToUnicode"_ostr
));
1259 CPPUNIT_ASSERT(pToUnicodeRef
);
1260 pToUnicode
= pToUnicodeRef
->LookupObject();
1265 CPPUNIT_ASSERT(pToUnicode
);
1266 auto pStream
= pToUnicode
->GetStream();
1267 CPPUNIT_ASSERT(pStream
);
1268 SvMemoryStream aObjectStream
;
1270 aZCodec
.BeginCompression();
1271 pStream
->GetMemory().Seek(0);
1272 aZCodec
.Decompress(pStream
->GetMemory(), aObjectStream
);
1273 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1274 aObjectStream
.Seek(0);
1275 OString
aCmap("7 beginbfchar\n"
1284 auto pStart
= static_cast<const char*>(aObjectStream
.GetData());
1285 const char* pEnd
= pStart
+ aObjectStream
.GetSize();
1286 auto it
= std::search(pStart
, pEnd
, aCmap
.getStr(), aCmap
.getStr() + aCmap
.getLength());
1287 CPPUNIT_ASSERT(it
!= pEnd
);
1291 /// Text extracting LTR text with ligatures.
1292 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf115117_1a
)
1295 // Import the bugdoc and export as PDF.
1296 saveAsPDF(u
"tdf115117-1.odt");
1298 // Parse the export result with pdfium.
1299 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1301 // The document has one page.
1302 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1303 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1304 CPPUNIT_ASSERT(pPdfPage
);
1306 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage
= pPdfPage
->getTextPage();
1307 CPPUNIT_ASSERT(pPdfTextPage
);
1309 // Extract the text from the page. This pdfium API is a bit higher level
1310 // than we want and might apply heuristic that give false positive, but it
1311 // is a good approximation in addition to the check in testTdf115117_1().
1312 int nChars
= pPdfTextPage
->countChars();
1313 CPPUNIT_ASSERT_EQUAL(44, nChars
);
1315 std::vector
<sal_uInt32
> aChars(nChars
);
1316 for (int i
= 0; i
< nChars
; i
++)
1317 aChars
[i
] = pPdfTextPage
->getUnicode(i
);
1318 OUString
aActualText(aChars
.data(), aChars
.size());
1319 CPPUNIT_ASSERT_EQUAL(u
"ti ti test ti\r\nti test fi fl ffi ffl test fi"_ustr
, aActualText
);
1323 /// Test extracting RTL text with ligatures.
1324 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf115117_2a
)
1327 // See the comments in testTdf115117_1a() for explanation.
1329 // Import the bugdoc and export as PDF.
1330 saveAsPDF(u
"tdf115117-2.odt");
1332 // Parse the export result with pdfium.
1333 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1335 // The document has one page.
1336 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1337 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1338 CPPUNIT_ASSERT(pPdfPage
);
1340 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage
= pPdfPage
->getTextPage();
1341 CPPUNIT_ASSERT(pPdfTextPage
);
1343 int nChars
= pPdfTextPage
->countChars();
1344 CPPUNIT_ASSERT_EQUAL(13, nChars
);
1346 std::vector
<sal_uInt32
> aChars(nChars
);
1347 for (int i
= 0; i
< nChars
; i
++)
1348 aChars
[i
] = pPdfTextPage
->getUnicode(i
);
1349 OUString
aActualText(aChars
.data(), aChars
.size());
1350 CPPUNIT_ASSERT_EQUAL(u
"\u0627\u0644 \u0628\u0627\u0644 \u0648\u0642\u0641 \u0627\u0644"_ustr
,
1355 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf154549
)
1357 // FIXME: On Windows, the number of chars is 4 instead of 3
1359 saveAsPDF(u
"tdf154549.odt");
1361 // Parse the export result with pdfium.
1362 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1364 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1365 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1366 CPPUNIT_ASSERT(pPdfPage
);
1368 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage
= pPdfPage
->getTextPage();
1369 CPPUNIT_ASSERT(pPdfTextPage
);
1371 int nChars
= pPdfTextPage
->countChars();
1373 CPPUNIT_ASSERT_EQUAL(1, nChars
);
1375 std::vector
<sal_uInt32
> aChars(nChars
);
1376 for (int i
= 0; i
< nChars
; i
++)
1377 aChars
[i
] = pPdfTextPage
->getUnicode(i
);
1378 OUString
aActualText(aChars
.data(), aChars
.size());
1380 // Without the fix in place, this test would have failed with
1383 CPPUNIT_ASSERT_EQUAL(u
"\u064a"_ustr
, aActualText
);
1387 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf150846
)
1389 // Without the fix in place, this test would have failed with
1390 // An uncaught exception of type com.sun.star.io.IOException
1391 saveAsPDF(u
"tdf150846.txt");
1393 // Parse the export result with pdfium.
1394 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1396 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1397 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1398 CPPUNIT_ASSERT(pPdfPage
);
1400 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage
= pPdfPage
->getTextPage();
1401 CPPUNIT_ASSERT(pPdfTextPage
);
1403 int nChars
= pPdfTextPage
->countChars();
1405 CPPUNIT_ASSERT_EQUAL(5, nChars
);
1407 std::vector
<sal_uInt32
> aChars(nChars
);
1408 for (int i
= 0; i
< nChars
; i
++)
1409 aChars
[i
] = pPdfTextPage
->getUnicode(i
);
1410 OUString
aActualText(aChars
.data(), aChars
.size());
1411 CPPUNIT_ASSERT_EQUAL(u
"hello"_ustr
, aActualText
);
1414 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf160401
)
1416 saveAsPDF(u
"tdf160401.pptx");
1418 // Parse the export result with pdfium.
1419 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1421 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1422 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1423 CPPUNIT_ASSERT(pPdfPage
);
1425 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage
= pPdfPage
->getTextPage();
1426 CPPUNIT_ASSERT(pPdfTextPage
);
1428 int nChars
= pPdfTextPage
->countChars();
1430 CPPUNIT_ASSERT_EQUAL(16, nChars
);
1432 std::vector
<sal_uInt32
> aChars(nChars
);
1433 for (int i
= 0; i
< nChars
; i
++)
1434 aChars
[i
] = pPdfTextPage
->getUnicode(i
);
1435 OUString
aActualText(aChars
.data(), aChars
.size());
1437 // Without the fix in place, this test would have failed with
1438 // - Expected: אאא בבב
1440 // - Actual : אאא בבב
1442 CPPUNIT_ASSERT_EQUAL(u
"אאא בבב\r\nאאא בבב"_ustr
, aActualText
);
1445 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf103492
)
1447 // Import the bugdoc and export as PDF.
1448 saveAsPDF(u
"tdf103492.odt");
1450 // Parse the export result with pdfium.
1451 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1453 // The document has two page.
1454 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
1455 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage1
= pPdfDocument
->openPage(/*nIndex=*/0);
1456 CPPUNIT_ASSERT(pPdfPage1
);
1458 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage1
= pPdfPage1
->getTextPage();
1459 CPPUNIT_ASSERT(pPdfTextPage1
);
1461 int nChars1
= pPdfTextPage1
->countChars();
1463 // Without the fix in place, this test would have failed with
1466 CPPUNIT_ASSERT_EQUAL(15, nChars1
);
1468 std::vector
<sal_uInt32
> aChars1(nChars1
);
1469 for (int i
= 0; i
< nChars1
; i
++)
1470 aChars1
[i
] = pPdfTextPage1
->getUnicode(i
);
1471 OUString
aActualText1(aChars1
.data(), aChars1
.size());
1472 CPPUNIT_ASSERT_EQUAL(u
"يوسف My name is"_ustr
, aActualText1
);
1474 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage2
= pPdfDocument
->openPage(/*nIndex=*/1);
1475 CPPUNIT_ASSERT(pPdfPage2
);
1477 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pPdfTextPage2
= pPdfPage2
->getTextPage();
1478 CPPUNIT_ASSERT(pPdfTextPage2
);
1480 int nChars2
= pPdfTextPage2
->countChars();
1482 CPPUNIT_ASSERT_EQUAL(15, nChars2
);
1484 std::vector
<sal_uInt32
> aChars2(nChars2
);
1485 for (int i
= 0; i
< nChars2
; i
++)
1486 aChars2
[i
] = pPdfTextPage2
->getUnicode(i
);
1487 OUString
aActualText2(aChars2
.data(), aChars2
.size());
1488 CPPUNIT_ASSERT_EQUAL(u
"My name is يوسف"_ustr
, aActualText2
);
1491 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf145274
)
1493 // Import the bugdoc and export as PDF.
1494 saveAsPDF(u
"tdf145274.docx");
1496 // Parse the export result with pdfium.
1497 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1499 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1501 auto pPage
= pPdfDocument
->openPage(0);
1502 CPPUNIT_ASSERT(pPage
);
1504 int nPageObjectCount
= pPage
->getObjectCount();
1506 // Without the fix in place, this test would have failed with
1509 CPPUNIT_ASSERT_EQUAL(6, nPageObjectCount
);
1511 auto pTextPage
= pPage
->getTextPage();
1513 for (int i
= 0; i
< nPageObjectCount
; ++i
)
1515 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPage
->getObject(i
);
1516 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Text
)
1519 CPPUNIT_ASSERT_EQUAL(11.0, pPageObject
->getFontSize());
1520 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFTextRenderMode::Fill
, pPageObject
->getTextRenderMode());
1521 CPPUNIT_ASSERT_EQUAL(COL_RED
, pPageObject
->getFillColor());
1525 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf156685
)
1527 // Import the bugdoc and export as PDF.
1528 saveAsPDF(u
"tdf156685.docx");
1530 // Parse the export result with pdfium.
1531 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1533 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1535 auto pPage
= pPdfDocument
->openPage(0);
1536 CPPUNIT_ASSERT(pPage
);
1538 int nPageObjectCount
= pPage
->getObjectCount();
1540 CPPUNIT_ASSERT_EQUAL(9, nPageObjectCount
);
1542 auto pTextPage
= pPage
->getTextPage();
1544 for (int i
= 0; i
< nPageObjectCount
; ++i
)
1546 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPage
->getObject(i
);
1547 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Text
)
1550 CPPUNIT_ASSERT_EQUAL(11.0, pPageObject
->getFontSize());
1551 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFTextRenderMode::Fill
, pPageObject
->getTextRenderMode());
1553 // Without the fix in place, this test would have failed with
1554 // - Expected: rgba[000000ff]
1555 // - Actual : rgba[ffffffff]
1556 CPPUNIT_ASSERT_EQUAL(COL_BLACK
, pPageObject
->getFillColor());
1560 /// Test writing ToUnicode CMAP for doubly encoded glyphs.
1561 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf66597_1
)
1564 // This requires Amiri font, if it is missing the test will fail.
1565 vcl::filter::PDFDocument aDocument
;
1566 load(u
"tdf66597-1.odt", aDocument
);
1569 // Get access to ToUnicode of the first font
1570 vcl::filter::PDFObjectElement
* pToUnicode
= nullptr;
1571 for (const auto& aElement
: aDocument
.GetElements())
1573 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1576 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1577 if (pType
&& pType
->GetValue() == "Font")
1580 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("BaseFont"_ostr
));
1581 auto aName
= pName
->GetValue().copy(7); // skip the subset id
1582 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected font name", "Amiri-Regular"_ostr
, aName
);
1584 auto pToUnicodeRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1585 pObject
->Lookup("ToUnicode"_ostr
));
1586 CPPUNIT_ASSERT(pToUnicodeRef
);
1587 pToUnicode
= pToUnicodeRef
->LookupObject();
1592 CPPUNIT_ASSERT(pToUnicode
);
1593 auto pStream
= pToUnicode
->GetStream();
1594 CPPUNIT_ASSERT(pStream
);
1595 SvMemoryStream aObjectStream
;
1597 aZCodec
.BeginCompression();
1598 pStream
->GetMemory().Seek(0);
1599 aZCodec
.Decompress(pStream
->GetMemory(), aObjectStream
);
1600 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1601 aObjectStream
.Seek(0);
1602 // The <01> is glyph id, <2044> is code point.
1603 // The document has two characters <2044><2215><2044>, but the font
1604 // reuses the same glyph for U+2044 and U+2215 so we should have a single
1605 // CMAP entry for the U+2044, and U+2215 will be handled with ActualText
1607 std::string
aCmap("1 beginbfchar\n"
1610 std::string
aData(static_cast<const char*>(aObjectStream
.GetData()),
1611 aObjectStream
.GetSize());
1612 auto nPos
= aData
.find(aCmap
);
1613 CPPUNIT_ASSERT(nPos
!= std::string::npos
);
1617 auto aPages
= aDocument
.GetPages();
1618 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1619 // Get page contents and stream.
1620 auto pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1621 CPPUNIT_ASSERT(pContents
);
1622 auto pStream
= pContents
->GetStream();
1623 CPPUNIT_ASSERT(pStream
);
1624 auto& rObjectStream
= pStream
->GetMemory();
1626 // Uncompress the stream.
1627 SvMemoryStream aUncompressed
;
1629 aZCodec
.BeginCompression();
1630 rObjectStream
.Seek(0);
1631 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1632 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1634 // Make sure the expected ActualText is present.
1635 std::string
aData(static_cast<const char*>(aUncompressed
.GetData()),
1636 aUncompressed
.GetSize());
1638 std::string
aActualText("/Span<</ActualText<");
1641 while ((nPos
= aData
.find(aActualText
, nPos
)) != std::string::npos
)
1644 nPos
+= aActualText
.length();
1646 CPPUNIT_ASSERT_EQUAL_MESSAGE("The should be one ActualText entry!", static_cast<size_t>(1),
1649 aActualText
= "/Span<</ActualText<FEFF2215>>>";
1650 nPos
= aData
.find(aActualText
);
1651 CPPUNIT_ASSERT_MESSAGE("ActualText not found!", nPos
!= std::string::npos
);
1656 /// Test writing ActualText for RTL many to one glyph to Unicode mapping.
1657 // This requires Reem Kufi font, if it is missing the test will fail.
1658 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf66597_2
)
1661 vcl::filter::PDFDocument aDocument
;
1662 load(u
"tdf66597-2.odt", aDocument
);
1665 // Get access to ToUnicode of the first font
1666 vcl::filter::PDFObjectElement
* pToUnicode
= nullptr;
1667 for (const auto& aElement
: aDocument
.GetElements())
1669 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1672 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1673 if (pType
&& pType
->GetValue() == "Font")
1676 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("BaseFont"_ostr
));
1677 auto aName
= pName
->GetValue().copy(7); // skip the subset id
1678 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected font name", "ReemKufi-Regular"_ostr
,
1681 auto pToUnicodeRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1682 pObject
->Lookup("ToUnicode"_ostr
));
1683 CPPUNIT_ASSERT(pToUnicodeRef
);
1684 pToUnicode
= pToUnicodeRef
->LookupObject();
1689 CPPUNIT_ASSERT(pToUnicode
);
1690 auto pStream
= pToUnicode
->GetStream();
1691 CPPUNIT_ASSERT(pStream
);
1692 SvMemoryStream aObjectStream
;
1694 aZCodec
.BeginCompression();
1695 pStream
->GetMemory().Seek(0);
1696 aZCodec
.Decompress(pStream
->GetMemory(), aObjectStream
);
1697 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1698 aObjectStream
.Seek(0);
1699 std::string
aCmap("8 beginbfchar\n"
1709 std::string
aData(static_cast<const char*>(aObjectStream
.GetData()),
1710 aObjectStream
.GetSize());
1711 auto nPos
= aData
.find(aCmap
);
1712 CPPUNIT_ASSERT(nPos
!= std::string::npos
);
1716 auto aPages
= aDocument
.GetPages();
1717 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1718 // Get page contents and stream.
1719 auto pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1720 CPPUNIT_ASSERT(pContents
);
1721 auto pStream
= pContents
->GetStream();
1722 CPPUNIT_ASSERT(pStream
);
1723 auto& rObjectStream
= pStream
->GetMemory();
1725 // Uncompress the stream.
1726 SvMemoryStream aUncompressed
;
1728 aZCodec
.BeginCompression();
1729 rObjectStream
.Seek(0);
1730 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1731 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1733 // Make sure the expected ActualText is present.
1734 std::string
aData(static_cast<const char*>(aUncompressed
.GetData()),
1735 aUncompressed
.GetSize());
1737 std::vector
<std::string
> aCodes({ "0632", "062C", "0628", "0623" });
1738 std::string
aActualText("/Span<</ActualText<");
1741 while ((nPos
= aData
.find(aActualText
, nPos
)) != std::string::npos
)
1744 nPos
+= aActualText
.length();
1746 CPPUNIT_ASSERT_EQUAL_MESSAGE("Number of ActualText entries does not match!", aCodes
.size(),
1749 for (const auto& aCode
: aCodes
)
1751 aActualText
= "/Span<</ActualText<FEFF" + aCode
+ ">>>";
1752 nPos
= aData
.find(aActualText
);
1753 CPPUNIT_ASSERT_MESSAGE("ActualText not found for " + aCode
, nPos
!= std::string::npos
);
1759 /// Test writing ActualText for LTR many to one glyph to Unicode mapping.
1760 // This requires Gentium Basic font, if it is missing the test will fail.
1761 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf66597_3
)
1764 vcl::filter::PDFDocument aDocument
;
1765 load(u
"tdf66597-3.odt", aDocument
);
1768 // Get access to ToUnicode of the first font
1769 vcl::filter::PDFObjectElement
* pToUnicode
= nullptr;
1770 for (const auto& aElement
: aDocument
.GetElements())
1772 auto pObject
= dynamic_cast<vcl::filter::PDFObjectElement
*>(aElement
.get());
1775 auto pType
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("Type"_ostr
));
1776 if (pType
&& pType
->GetValue() == "Font")
1779 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObject
->Lookup("BaseFont"_ostr
));
1780 auto aName
= pName
->GetValue().copy(7); // skip the subset id
1781 CPPUNIT_ASSERT_EQUAL_MESSAGE("Unexpected font name", "GentiumBasic"_ostr
, aName
);
1783 auto pToUnicodeRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
1784 pObject
->Lookup("ToUnicode"_ostr
));
1785 CPPUNIT_ASSERT(pToUnicodeRef
);
1786 pToUnicode
= pToUnicodeRef
->LookupObject();
1791 CPPUNIT_ASSERT(pToUnicode
);
1792 auto pStream
= pToUnicode
->GetStream();
1793 CPPUNIT_ASSERT(pStream
);
1794 SvMemoryStream aObjectStream
;
1796 aZCodec
.BeginCompression();
1797 pStream
->GetMemory().Seek(0);
1798 aZCodec
.Decompress(pStream
->GetMemory(), aObjectStream
);
1799 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1800 aObjectStream
.Seek(0);
1801 std::string
aCmap("2 beginbfchar\n"
1802 "<01> <1ECB0331030B>\n"
1805 std::string
aData(static_cast<const char*>(aObjectStream
.GetData()),
1806 aObjectStream
.GetSize());
1807 auto nPos
= aData
.find(aCmap
);
1808 CPPUNIT_ASSERT(nPos
!= std::string::npos
);
1812 auto aPages
= aDocument
.GetPages();
1813 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
1814 // Get page contents and stream.
1815 auto pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
1816 CPPUNIT_ASSERT(pContents
);
1817 auto pStream
= pContents
->GetStream();
1818 CPPUNIT_ASSERT(pStream
);
1819 auto& rObjectStream
= pStream
->GetMemory();
1821 // Uncompress the stream.
1822 SvMemoryStream aUncompressed
;
1824 aZCodec
.BeginCompression();
1825 rObjectStream
.Seek(0);
1826 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
1827 CPPUNIT_ASSERT(aZCodec
.EndCompression());
1829 // Make sure the expected ActualText is present.
1830 std::string
aData(static_cast<const char*>(aUncompressed
.GetData()),
1831 aUncompressed
.GetSize());
1833 std::string
aActualText("/Span<</ActualText<FEFF1ECB0331030B>>>");
1836 while ((nPos
= aData
.find(aActualText
, nPos
)) != std::string::npos
)
1839 nPos
+= aActualText
.length();
1841 CPPUNIT_ASSERT_EQUAL_MESSAGE("Number of ActualText entries does not match!",
1842 static_cast<size_t>(4), nCount
);
1847 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf105954
)
1849 // Import the bugdoc and export as PDF.
1850 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
1851 { { "ReduceImageResolution", uno::Any(true) },
1852 { "MaxImageResolution", uno::Any(static_cast<sal_Int32
>(300)) } }));
1853 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
1854 saveAsPDF(u
"tdf105954.odt");
1856 // Parse the export result with pdfium.
1857 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1859 // The document has one page.
1860 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1861 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1862 CPPUNIT_ASSERT(pPdfPage
);
1864 // There is a single image on the page.
1865 int nPageObjectCount
= pPdfPage
->getObjectCount();
1866 CPPUNIT_ASSERT_EQUAL(1, nPageObjectCount
);
1868 // Check width of the image.
1869 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(/*index=*/0);
1870 Size aMeta
= pPageObject
->getImageSize(*pPdfPage
);
1871 // This was 2000, i.e. the 'reduce to 300 DPI' request was ignored.
1872 // This is now around 238 (228 on macOS).
1873 CPPUNIT_ASSERT_LESS(static_cast<tools::Long
>(250), aMeta
.getWidth());
1876 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf157679
)
1878 // Import the bugdoc and export as PDF.
1879 saveAsPDF(u
"tdf157679.pptx");
1880 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1882 // The document has one page.
1883 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1885 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1886 CPPUNIT_ASSERT(pPdfPage
);
1888 // Without the fix in place, this test would have failed with
1891 CPPUNIT_ASSERT_EQUAL(3, pPdfPage
->getObjectCount());
1893 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
1894 int nPageObjectCount
= pPdfPage
->getObjectCount();
1895 for (int i
= 0; i
< nPageObjectCount
; ++i
)
1897 // Check there are not Text objects
1898 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
1899 CPPUNIT_ASSERT(pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Text
);
1903 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf128445
)
1905 // Import the bugdoc and export as PDF.
1906 saveAsPDF(u
"tdf128445.odp");
1907 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1909 // The document has one page.
1910 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1912 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1913 CPPUNIT_ASSERT(pPdfPage
);
1915 // Without the fix in place, this test would have failed with
1918 CPPUNIT_ASSERT_EQUAL(7, pPdfPage
->getObjectCount());
1921 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf128630
)
1923 // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
1924 // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
1925 if (!IsDefaultDPI())
1928 // Import the bugdoc and export as PDF.
1929 saveAsPDF(u
"tdf128630.odp");
1930 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1932 // The document has one page.
1933 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
1935 // Assert the size of the only bitmap on the page.
1936 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1937 CPPUNIT_ASSERT(pPdfPage
);
1938 int nPageObjectCount
= pPdfPage
->getObjectCount();
1939 for (int i
= 0; i
< nPageObjectCount
; ++i
)
1941 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
1942 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Image
)
1945 std::unique_ptr
<vcl::pdf::PDFiumBitmap
> pBitmap
= pPageObject
->getImageBitmap();
1946 CPPUNIT_ASSERT(pBitmap
);
1947 int nWidth
= pBitmap
->getWidth();
1948 // Without the accompanying fix in place, this test would have failed with:
1951 // i.e. the rotated + scaled arrow was more thin than it should be.
1952 CPPUNIT_ASSERT_DOUBLES_EQUAL(466, nWidth
, 1);
1953 int nHeight
= pBitmap
->getHeight();
1954 CPPUNIT_ASSERT_EQUAL(nWidth
, nHeight
);
1958 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf106702
)
1960 // Import the bugdoc and export as PDF.
1961 saveAsPDF(u
"tdf106702.odt");
1963 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
1965 // The document has two pages.
1966 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
1968 // First page already has the correct image position.
1969 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
1970 CPPUNIT_ASSERT(pPdfPage
);
1972 int nPageObjectCount
= pPdfPage
->getObjectCount();
1973 for (int i
= 0; i
< nPageObjectCount
; ++i
)
1975 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
1976 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Image
)
1979 // Top, but upside down.
1980 nExpected
= pPageObject
->getBounds().getMaxY();
1984 // Second page had an incorrect image position.
1985 pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/1);
1986 CPPUNIT_ASSERT(pPdfPage
);
1988 nPageObjectCount
= pPdfPage
->getObjectCount();
1989 for (int i
= 0; i
< nPageObjectCount
; ++i
)
1991 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
1992 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Image
)
1995 // Top, but upside down.
1996 nActual
= pPageObject
->getBounds().getMaxY();
2000 // This failed, vertical pos is 818 points, was 1674 (outside visible page
2002 CPPUNIT_ASSERT_EQUAL(nExpected
, nActual
);
2005 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf113143
)
2007 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence({
2008 { "ExportNotesPages", uno::Any(true) },
2009 // ReduceImageResolution is on by default and that hides the bug we
2011 { "ReduceImageResolution", uno::Any(false) },
2012 // Set a custom PDF version.
2013 { "SelectPdfVersion", uno::Any(static_cast<sal_Int32
>(16)) },
2015 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2016 saveAsPDF(u
"tdf113143.odp");
2018 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
2020 // The document has two pages.
2021 CPPUNIT_ASSERT_EQUAL(2, pPdfDocument
->getPageCount());
2023 // First has the original (larger) image.
2024 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
2025 CPPUNIT_ASSERT(pPdfPage
);
2027 int nPageObjectCount
= pPdfPage
->getObjectCount();
2028 for (int i
= 0; i
< nPageObjectCount
; ++i
)
2030 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
2031 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Image
)
2034 nLarger
= pPageObject
->getBounds().getWidth();
2038 // Second page has the scaled (smaller) image.
2039 pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/1);
2040 CPPUNIT_ASSERT(pPdfPage
);
2042 nPageObjectCount
= pPdfPage
->getObjectCount();
2043 for (int i
= 0; i
< nPageObjectCount
; ++i
)
2045 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
2046 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Image
)
2049 nSmaller
= pPageObject
->getBounds().getWidth();
2053 // This failed, both were 319, now nSmaller is 169.
2054 CPPUNIT_ASSERT_LESS(nLarger
, nSmaller
);
2056 // The following check used to fail in the past, header was "%PDF-1.5":
2058 OString
aExpectedHeader("%PDF-1.6"_ostr
);
2059 OString
aHeader(read_uInt8s_ToOString(maMemory
, aExpectedHeader
.getLength()));
2060 CPPUNIT_ASSERT_EQUAL(aExpectedHeader
, aHeader
);
2063 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testForcePoint71
)
2065 // I just care it doesn't crash
2066 // numerous Zip errors are detected now and libetonyek cannot RepairPackage
2067 CPPUNIT_ASSERT_ASSERTION_FAIL(loadFromFile(u
"forcepoint71.key"));
2070 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testForcePoint80
)
2072 // printing asserted in SwCellFrame::FindStartEndOfRowSpanCell
2073 saveAsPDF(u
"forcepoint80-1.rtf");
2076 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testForcePoint3
)
2078 // printing asserted in SwFrame::GetNextSctLeaf()
2079 saveAsPDF(u
"flowframe_null_ptr_deref.sample");
2082 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf162586
)
2084 // Without the fix in place, this test would have crashed
2085 saveAsPDF(u
"tdf162586.odt");
2088 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf84283
)
2090 // Without the fix in place, this test would have crashed
2091 saveAsPDF(u
"tdf84283.doc");
2094 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf115262
)
2096 saveAsPDF(u
"tdf115262.ods");
2097 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
2098 CPPUNIT_ASSERT_EQUAL(8, pPdfDocument
->getPageCount());
2100 // Get the 6th page.
2101 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/5);
2102 CPPUNIT_ASSERT(pPdfPage
);
2104 // Look up the position of the first image and the 400th row.
2105 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
2106 int nPageObjectCount
= pPdfPage
->getObjectCount();
2107 int nFirstImageTop
= 0;
2109 for (int i
= 0; i
< nPageObjectCount
; ++i
)
2111 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
2112 // Top, but upside down.
2113 float fTop
= pPageObject
->getBounds().getMaxY();
2115 if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Image
)
2117 nFirstImageTop
= fTop
;
2119 else if (pPageObject
->getType() == vcl::pdf::PDFPageObjectType::Text
)
2121 OUString sText
= pPageObject
->getText(pTextPage
);
2126 // Make sure that the top of the "400" is below the top of the image (in
2127 // bottom-right-corner-based PDF coordinates).
2128 // This was: expected less than 144, actual is 199.
2129 CPPUNIT_ASSERT_LESS(nFirstImageTop
, nRowTop
);
2132 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf121962
)
2134 saveAsPDF(u
"tdf121962.odt");
2135 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
2136 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
2138 // Get the first page
2139 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
2140 CPPUNIT_ASSERT(pPdfPage
);
2141 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
2143 // Make sure the table sum is displayed as "0", not faulty expression.
2144 int nPageObjectCount
= pPdfPage
->getObjectCount();
2145 for (int i
= 0; i
< nPageObjectCount
; ++i
)
2147 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
2148 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Text
)
2150 OUString sText
= pPageObject
->getText(pTextPage
);
2151 CPPUNIT_ASSERT(sText
!= "** Expression is faulty **");
2155 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf139065
)
2157 saveAsPDF(u
"tdf139065.odt");
2158 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
2159 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
2160 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
2161 CPPUNIT_ASSERT(pPdfPage
);
2163 // Without the fix in place, this test would have failed with
2166 CPPUNIT_ASSERT_EQUAL(15, pPdfPage
->getObjectCount());
2169 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf157816
)
2172 uno::Sequence
<beans::PropertyValue
> aFilterData(
2173 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2174 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2176 vcl::filter::PDFDocument aDocument
;
2177 load(u
"tdf157816.fodt", aDocument
);
2179 // The document has one page.
2180 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2181 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
2183 vcl::filter::PDFObjectElement
* pDocument(nullptr);
2184 for (const auto& rDocElement
: aDocument
.GetElements())
2186 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2189 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
2190 if (pType1
&& pType1
->GetValue() == "StructElem")
2192 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
2193 if (pS1
&& pS1
->GetValue() == "Document")
2195 pDocument
= pObject1
;
2199 CPPUNIT_ASSERT(pDocument
);
2201 auto pKidsD
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pDocument
->Lookup("K"_ostr
));
2202 CPPUNIT_ASSERT(pKidsD
);
2203 // assume there are no MCID ref at this level
2204 auto pKidsDv
= pKidsD
->GetElements();
2205 auto pRefKidD2
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsDv
[2]);
2206 CPPUNIT_ASSERT(pRefKidD2
);
2207 auto pObjectD2
= pRefKidD2
->LookupObject();
2208 CPPUNIT_ASSERT(pObjectD2
);
2209 auto pTypeD2
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD2
->Lookup("Type"_ostr
));
2210 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD2
->GetValue());
2211 auto pSD2
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD2
->Lookup("S"_ostr
));
2212 CPPUNIT_ASSERT_EQUAL("Text#20body"_ostr
, pSD2
->GetValue());
2214 auto pKidsD2
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD2
->Lookup("K"_ostr
));
2215 CPPUNIT_ASSERT(pKidsD2
);
2216 auto pKidsD2v
= pKidsD2
->GetElements();
2217 auto pRefKidD20
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[0]);
2219 CPPUNIT_ASSERT(!pRefKidD20
);
2220 auto pRefKidD21
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[1]);
2222 CPPUNIT_ASSERT(!pRefKidD21
);
2224 auto pRefKidD22
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[2]);
2225 CPPUNIT_ASSERT(pRefKidD22
);
2226 auto pObjectD22
= pRefKidD22
->LookupObject();
2227 CPPUNIT_ASSERT(pObjectD22
);
2228 auto pTypeD22
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD22
->Lookup("Type"_ostr
));
2229 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD22
->GetValue());
2230 auto pSD22
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD22
->Lookup("S"_ostr
));
2231 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD22
->GetValue());
2233 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD22
->Lookup("K"_ostr
));
2236 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2238 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2239 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2248 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2249 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2250 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2251 pObjR
->LookupElement("Obj"_ostr
));
2252 auto pAnnot
= pAnnotRef
->LookupObject();
2254 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2255 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2257 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2258 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2259 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2260 pAnnot
->Lookup("Contents"_ostr
));
2261 CPPUNIT_ASSERT_EQUAL(
2262 u
"Error: Reference source not found"_ustr
,
2263 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2264 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2265 pAnnot
->Lookup("StructParent"_ostr
));
2266 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2268 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2269 CPPUNIT_ASSERT(pARect
);
2270 const auto& rElements
= pARect
->GetElements();
2271 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2272 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2273 CPPUNIT_ASSERT(pNumL
);
2274 CPPUNIT_ASSERT_DOUBLES_EQUAL(95.143, pNumL
->GetValue(), 1e-3);
2275 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2276 CPPUNIT_ASSERT(pNumT
);
2277 CPPUNIT_ASSERT_DOUBLES_EQUAL(674.589, pNumT
->GetValue(), 1e-3);
2278 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2279 CPPUNIT_ASSERT(pNumR
);
2280 // this changed to the end of the text, not the start of the fly
2281 CPPUNIT_ASSERT_DOUBLES_EQUAL(187.157, pNumR
->GetValue(), 1e-3);
2282 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2283 CPPUNIT_ASSERT(pNumB
);
2284 CPPUNIT_ASSERT_DOUBLES_EQUAL(688.389, pNumB
->GetValue(), 1e-3);
2287 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2288 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2291 auto pRefKidD23
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[3]);
2292 CPPUNIT_ASSERT(pRefKidD23
);
2293 auto pObjectD23
= pRefKidD23
->LookupObject();
2294 CPPUNIT_ASSERT(pObjectD23
);
2295 auto pTypeD23
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD23
->Lookup("Type"_ostr
));
2296 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD23
->GetValue());
2297 auto pSD23
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD23
->Lookup("S"_ostr
));
2298 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD23
->GetValue());
2300 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD23
->Lookup("K"_ostr
));
2303 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2305 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2306 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2315 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2316 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2317 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2318 pObjR
->LookupElement("Obj"_ostr
));
2319 auto pAnnot
= pAnnotRef
->LookupObject();
2321 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2322 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2324 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2325 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2326 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2327 pAnnot
->Lookup("Contents"_ostr
));
2328 CPPUNIT_ASSERT_EQUAL(
2329 u
"Error: Reference source not found"_ustr
,
2330 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2331 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2332 pAnnot
->Lookup("StructParent"_ostr
));
2333 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2335 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2336 CPPUNIT_ASSERT(pARect
);
2337 const auto& rElements
= pARect
->GetElements();
2338 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2339 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2340 CPPUNIT_ASSERT(pNumL
);
2341 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.693, pNumL
->GetValue(), 1e-3);
2342 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2343 CPPUNIT_ASSERT(pNumT
);
2344 CPPUNIT_ASSERT_DOUBLES_EQUAL(660.789, pNumT
->GetValue(), 1e-3);
2345 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2346 CPPUNIT_ASSERT(pNumR
);
2347 CPPUNIT_ASSERT_DOUBLES_EQUAL(146.107, pNumR
->GetValue(), 1e-3);
2348 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2349 CPPUNIT_ASSERT(pNumB
);
2350 CPPUNIT_ASSERT_DOUBLES_EQUAL(674.589, pNumB
->GetValue(), 1e-3);
2353 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2354 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2357 auto pRefKidD24
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[4]);
2358 CPPUNIT_ASSERT(pRefKidD24
);
2359 auto pObjectD24
= pRefKidD24
->LookupObject();
2360 CPPUNIT_ASSERT(pObjectD24
);
2361 auto pTypeD24
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD24
->Lookup("Type"_ostr
));
2362 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD24
->GetValue());
2363 auto pSD24
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD24
->Lookup("S"_ostr
));
2364 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD24
->GetValue());
2366 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD24
->Lookup("K"_ostr
));
2369 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2371 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2372 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2381 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2382 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2383 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2384 pObjR
->LookupElement("Obj"_ostr
));
2385 auto pAnnot
= pAnnotRef
->LookupObject();
2387 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2388 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2390 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2391 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2392 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2393 pAnnot
->Lookup("Contents"_ostr
));
2394 CPPUNIT_ASSERT_EQUAL(
2395 u
"Error: Reference source not found"_ustr
,
2396 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2397 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2398 pAnnot
->Lookup("StructParent"_ostr
));
2399 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2401 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2402 CPPUNIT_ASSERT(pARect
);
2403 const auto& rElements
= pARect
->GetElements();
2404 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2405 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2406 CPPUNIT_ASSERT(pNumL
);
2407 CPPUNIT_ASSERT_DOUBLES_EQUAL(146.043, pNumL
->GetValue(), 1e-3);
2408 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2409 CPPUNIT_ASSERT(pNumT
);
2410 CPPUNIT_ASSERT_DOUBLES_EQUAL(660.789, pNumT
->GetValue(), 1e-3);
2411 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2412 CPPUNIT_ASSERT(pNumR
);
2413 CPPUNIT_ASSERT_DOUBLES_EQUAL(179.357, pNumR
->GetValue(), 1e-3);
2414 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2415 CPPUNIT_ASSERT(pNumB
);
2416 CPPUNIT_ASSERT_DOUBLES_EQUAL(674.589, pNumB
->GetValue(), 1e-3);
2419 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2420 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2423 auto pRefKidD25
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[5]);
2424 CPPUNIT_ASSERT(pRefKidD25
);
2425 auto pObjectD25
= pRefKidD25
->LookupObject();
2426 CPPUNIT_ASSERT(pObjectD25
);
2427 auto pTypeD25
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD25
->Lookup("Type"_ostr
));
2428 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD25
->GetValue());
2429 auto pSD25
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD25
->Lookup("S"_ostr
));
2430 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD25
->GetValue());
2432 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD25
->Lookup("K"_ostr
));
2435 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2437 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2438 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2447 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2448 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2449 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2450 pObjR
->LookupElement("Obj"_ostr
));
2451 auto pAnnot
= pAnnotRef
->LookupObject();
2453 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2454 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2456 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2457 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2458 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2459 pAnnot
->Lookup("Contents"_ostr
));
2460 CPPUNIT_ASSERT_EQUAL(
2461 u
"Error: Reference source not found"_ustr
,
2462 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2463 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2464 pAnnot
->Lookup("StructParent"_ostr
));
2465 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2467 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2468 CPPUNIT_ASSERT(pARect
);
2469 const auto& rElements
= pARect
->GetElements();
2470 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2471 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2472 CPPUNIT_ASSERT(pNumL
);
2473 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.693, pNumL
->GetValue(), 1e-3);
2474 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2475 CPPUNIT_ASSERT(pNumT
);
2476 CPPUNIT_ASSERT_DOUBLES_EQUAL(646.989, pNumT
->GetValue(), 1e-3);
2477 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2478 CPPUNIT_ASSERT(pNumR
);
2479 CPPUNIT_ASSERT_DOUBLES_EQUAL(174.757, pNumR
->GetValue(), 1e-3);
2480 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2481 CPPUNIT_ASSERT(pNumB
);
2482 CPPUNIT_ASSERT_DOUBLES_EQUAL(660.789, pNumB
->GetValue(), 1e-3);
2485 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2486 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2489 auto pRefKidD26
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[6]);
2490 CPPUNIT_ASSERT(pRefKidD26
);
2491 auto pObjectD26
= pRefKidD26
->LookupObject();
2492 CPPUNIT_ASSERT(pObjectD26
);
2493 auto pTypeD26
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD26
->Lookup("Type"_ostr
));
2494 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD26
->GetValue());
2495 auto pSD26
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD26
->Lookup("S"_ostr
));
2496 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD26
->GetValue());
2498 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD26
->Lookup("K"_ostr
));
2501 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2503 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2504 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2513 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2514 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2515 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2516 pObjR
->LookupElement("Obj"_ostr
));
2517 auto pAnnot
= pAnnotRef
->LookupObject();
2519 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2520 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2522 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2523 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2524 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2525 pAnnot
->Lookup("Contents"_ostr
));
2526 CPPUNIT_ASSERT_EQUAL(
2527 u
"Error: Reference source not found"_ustr
,
2528 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2529 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2530 pAnnot
->Lookup("StructParent"_ostr
));
2531 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2533 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2534 CPPUNIT_ASSERT(pARect
);
2535 const auto& rElements
= pARect
->GetElements();
2536 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2537 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2538 CPPUNIT_ASSERT(pNumL
);
2539 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.693, pNumL
->GetValue(), 1e-3);
2540 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2541 CPPUNIT_ASSERT(pNumT
);
2542 CPPUNIT_ASSERT_DOUBLES_EQUAL(633.189, pNumT
->GetValue(), 1e-3);
2543 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2544 CPPUNIT_ASSERT(pNumR
);
2545 CPPUNIT_ASSERT_DOUBLES_EQUAL(86.757, pNumR
->GetValue(), 1e-3);
2546 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2547 CPPUNIT_ASSERT(pNumB
);
2548 CPPUNIT_ASSERT_DOUBLES_EQUAL(646.989, pNumB
->GetValue(), 1e-3);
2551 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2552 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2555 auto pRefKidD27
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD2v
[7]);
2557 CPPUNIT_ASSERT(!pRefKidD27
);
2559 // the problem was that in addition to the 5 links with SE there were 3 more
2560 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
2561 CPPUNIT_ASSERT(pAnnots
);
2562 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(5), pAnnots
->GetElements().size());
2565 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf157816Link
)
2568 uno::Sequence
<beans::PropertyValue
> aFilterData(
2569 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2570 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2572 vcl::filter::PDFDocument aDocument
;
2573 load(u
"LinkWithFly.fodt", aDocument
);
2575 // The document has one page.
2576 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2577 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
2579 vcl::filter::PDFObjectElement
* pDocument(nullptr);
2580 for (const auto& rDocElement
: aDocument
.GetElements())
2582 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2585 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
2586 if (pType1
&& pType1
->GetValue() == "StructElem")
2588 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
2589 if (pS1
&& pS1
->GetValue() == "Document")
2591 pDocument
= pObject1
;
2595 CPPUNIT_ASSERT(pDocument
);
2597 auto pKidsD
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pDocument
->Lookup("K"_ostr
));
2598 CPPUNIT_ASSERT(pKidsD
);
2599 // assume there are no MCID ref at this level
2600 auto pKidsDv
= pKidsD
->GetElements();
2601 auto pRefKidD0
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsDv
[0]);
2602 CPPUNIT_ASSERT(pRefKidD0
);
2603 auto pObjectD0
= pRefKidD0
->LookupObject();
2604 CPPUNIT_ASSERT(pObjectD0
);
2605 auto pTypeD0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD0
->Lookup("Type"_ostr
));
2606 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD0
->GetValue());
2607 auto pSD0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD0
->Lookup("S"_ostr
));
2608 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pSD0
->GetValue());
2610 auto pKidsD0
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD0
->Lookup("K"_ostr
));
2611 CPPUNIT_ASSERT(pKidsD0
);
2612 auto pKidsD0v
= pKidsD0
->GetElements();
2614 auto pRefKidD00
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[0]);
2615 CPPUNIT_ASSERT(pRefKidD00
);
2616 auto pObjectD00
= pRefKidD00
->LookupObject();
2617 CPPUNIT_ASSERT(pObjectD00
);
2618 auto pTypeD00
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD00
->Lookup("Type"_ostr
));
2619 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD00
->GetValue());
2620 auto pSD00
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD00
->Lookup("S"_ostr
));
2621 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD00
->GetValue());
2623 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD00
->Lookup("K"_ostr
));
2626 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2628 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2629 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2638 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2639 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2640 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2641 pObjR
->LookupElement("Obj"_ostr
));
2642 auto pAnnot
= pAnnotRef
->LookupObject();
2644 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2645 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2647 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2648 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2649 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2650 pAnnot
->Lookup("Contents"_ostr
));
2651 CPPUNIT_ASSERT_EQUAL(
2652 u
"https://www.mozilla.org/en-US/firefox/119.0/releasenotes/"_ustr
,
2653 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2654 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2655 pAnnot
->Lookup("StructParent"_ostr
));
2656 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2658 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2659 CPPUNIT_ASSERT(pARect
);
2660 const auto& rElements
= pARect
->GetElements();
2661 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2662 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2663 CPPUNIT_ASSERT(pNumL
);
2664 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.693, pNumL
->GetValue(), 1e-3);
2665 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2666 CPPUNIT_ASSERT(pNumT
);
2667 CPPUNIT_ASSERT_DOUBLES_EQUAL(771.389, pNumT
->GetValue(), 1e-3);
2668 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2669 CPPUNIT_ASSERT(pNumR
);
2670 // this changed to the end of the text, not the start of the fly
2671 CPPUNIT_ASSERT_DOUBLES_EQUAL(191.657, pNumR
->GetValue(), 1e-3);
2672 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2673 CPPUNIT_ASSERT(pNumB
);
2674 CPPUNIT_ASSERT_DOUBLES_EQUAL(785.189, pNumB
->GetValue(), 1e-3);
2677 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2678 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2681 auto pRefKidD01
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[1]);
2682 CPPUNIT_ASSERT(pRefKidD01
);
2683 auto pObjectD01
= pRefKidD01
->LookupObject();
2684 CPPUNIT_ASSERT(pObjectD01
);
2685 auto pTypeD01
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD01
->Lookup("Type"_ostr
));
2686 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD01
->GetValue());
2687 auto pSD01
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD01
->Lookup("S"_ostr
));
2688 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD01
->GetValue());
2690 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD01
->Lookup("K"_ostr
));
2693 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2695 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2696 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2705 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2706 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2707 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2708 pObjR
->LookupElement("Obj"_ostr
));
2709 auto pAnnot
= pAnnotRef
->LookupObject();
2711 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2712 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2714 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2715 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2716 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2717 pAnnot
->Lookup("Contents"_ostr
));
2718 CPPUNIT_ASSERT_EQUAL(
2719 u
"https://www.mozilla.org/en-US/firefox/119.0/releasenotes/"_ustr
,
2720 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2721 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2722 pAnnot
->Lookup("StructParent"_ostr
));
2723 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2725 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2726 CPPUNIT_ASSERT(pARect
);
2727 const auto& rElements
= pARect
->GetElements();
2728 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2729 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2730 CPPUNIT_ASSERT(pNumL
);
2731 CPPUNIT_ASSERT_DOUBLES_EQUAL(387.843, pNumL
->GetValue(), 1e-3);
2732 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2733 CPPUNIT_ASSERT(pNumT
);
2734 CPPUNIT_ASSERT_DOUBLES_EQUAL(771.389, pNumT
->GetValue(), 1e-3);
2735 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2736 CPPUNIT_ASSERT(pNumR
);
2737 // this changed to the end of the text, not the start of the fly
2738 CPPUNIT_ASSERT_DOUBLES_EQUAL(534.407, pNumR
->GetValue(), 1e-3);
2739 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2740 CPPUNIT_ASSERT(pNumB
);
2741 CPPUNIT_ASSERT_DOUBLES_EQUAL(785.189, pNumB
->GetValue(), 1e-3);
2744 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2745 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2748 auto pRefKidD02
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[2]);
2749 CPPUNIT_ASSERT(pRefKidD02
);
2750 auto pObjectD02
= pRefKidD02
->LookupObject();
2751 CPPUNIT_ASSERT(pObjectD02
);
2752 auto pTypeD02
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD02
->Lookup("Type"_ostr
));
2753 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD02
->GetValue());
2754 auto pSD02
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD02
->Lookup("S"_ostr
));
2755 CPPUNIT_ASSERT_EQUAL("Figure"_ostr
, pSD02
->GetValue());
2757 auto pRefKidD1
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsDv
[1]);
2758 CPPUNIT_ASSERT(pRefKidD1
);
2759 auto pObjectD1
= pRefKidD1
->LookupObject();
2760 CPPUNIT_ASSERT(pObjectD1
);
2761 auto pTypeD1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD1
->Lookup("Type"_ostr
));
2762 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD1
->GetValue());
2763 auto pSD1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD1
->Lookup("S"_ostr
));
2764 CPPUNIT_ASSERT_EQUAL("Standard"_ostr
, pSD1
->GetValue());
2766 auto pKidsD1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD1
->Lookup("K"_ostr
));
2767 CPPUNIT_ASSERT(pKidsD1
);
2768 auto pKidsD1v
= pKidsD1
->GetElements();
2770 auto pRefKidD10
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD1v
[0]);
2771 CPPUNIT_ASSERT(pRefKidD10
);
2772 auto pObjectD10
= pRefKidD10
->LookupObject();
2773 CPPUNIT_ASSERT(pObjectD10
);
2774 auto pTypeD10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD10
->Lookup("Type"_ostr
));
2775 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD10
->GetValue());
2776 auto pSD10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD10
->Lookup("S"_ostr
));
2777 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD10
->GetValue());
2779 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD10
->Lookup("K"_ostr
));
2782 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2784 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2785 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2794 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2795 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2796 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2797 pObjR
->LookupElement("Obj"_ostr
));
2798 auto pAnnot
= pAnnotRef
->LookupObject();
2800 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2801 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2803 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2804 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2805 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2806 pAnnot
->Lookup("Contents"_ostr
));
2807 CPPUNIT_ASSERT_EQUAL(
2808 u
"https://www.mozilla.org/en-US/firefox/118.0/releasenotes/"_ustr
,
2809 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2810 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2811 pAnnot
->Lookup("StructParent"_ostr
));
2812 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2814 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2815 CPPUNIT_ASSERT(pARect
);
2816 const auto& rElements
= pARect
->GetElements();
2817 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2818 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2819 CPPUNIT_ASSERT(pNumL
);
2820 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.693, pNumL
->GetValue(), 1e-3);
2821 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2822 CPPUNIT_ASSERT(pNumT
);
2823 CPPUNIT_ASSERT_DOUBLES_EQUAL(757.589, pNumT
->GetValue(), 1e-3);
2824 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2825 CPPUNIT_ASSERT(pNumR
);
2826 // this changed to the end of the text, not the start of the fly
2827 CPPUNIT_ASSERT_DOUBLES_EQUAL(191.657, pNumR
->GetValue(), 1e-3);
2828 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2829 CPPUNIT_ASSERT(pNumB
);
2830 CPPUNIT_ASSERT_DOUBLES_EQUAL(771.389, pNumB
->GetValue(), 1e-3);
2833 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2834 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2837 auto pRefKidD11
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD1v
[1]);
2838 CPPUNIT_ASSERT(pRefKidD11
);
2839 auto pObjectD11
= pRefKidD11
->LookupObject();
2840 CPPUNIT_ASSERT(pObjectD11
);
2841 auto pTypeD11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD11
->Lookup("Type"_ostr
));
2842 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD11
->GetValue());
2843 auto pSD11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD11
->Lookup("S"_ostr
));
2844 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD11
->GetValue());
2846 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD11
->Lookup("K"_ostr
));
2849 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
2851 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
2852 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
2861 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
2862 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
2863 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
2864 pObjR
->LookupElement("Obj"_ostr
));
2865 auto pAnnot
= pAnnotRef
->LookupObject();
2867 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
2868 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
2870 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
2871 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
2872 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
2873 pAnnot
->Lookup("Contents"_ostr
));
2874 CPPUNIT_ASSERT_EQUAL(
2875 u
"https://www.mozilla.org/en-US/firefox/118.0/releasenotes/"_ustr
,
2876 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
2877 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
2878 pAnnot
->Lookup("StructParent"_ostr
));
2879 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
2881 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
2882 CPPUNIT_ASSERT(pARect
);
2883 const auto& rElements
= pARect
->GetElements();
2884 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
2885 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
2886 CPPUNIT_ASSERT(pNumL
);
2887 CPPUNIT_ASSERT_DOUBLES_EQUAL(387.843, pNumL
->GetValue(), 1e-3);
2888 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
2889 CPPUNIT_ASSERT(pNumT
);
2890 CPPUNIT_ASSERT_DOUBLES_EQUAL(757.589, pNumT
->GetValue(), 1e-3);
2891 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
2892 CPPUNIT_ASSERT(pNumR
);
2893 // this changed to the end of the text, not the start of the fly
2894 CPPUNIT_ASSERT_DOUBLES_EQUAL(534.407, pNumR
->GetValue(), 1e-3);
2895 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
2896 CPPUNIT_ASSERT(pNumB
);
2897 CPPUNIT_ASSERT_DOUBLES_EQUAL(771.389, pNumB
->GetValue(), 1e-3);
2900 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
2901 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
2904 // the problem was that in addition to the 4 links with SE there was 1 more
2905 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
2906 CPPUNIT_ASSERT(pAnnots
);
2907 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), pAnnots
->GetElements().size());
2910 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf142133
)
2912 vcl::filter::PDFDocument aDocument
;
2913 load(u
"tdf142133.docx", aDocument
);
2915 // The document has one page.
2916 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2917 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
2919 auto pAnnots
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
2920 CPPUNIT_ASSERT(pAnnots
);
2922 // There should be one annotation
2923 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), pAnnots
->GetElements().size());
2924 auto pAnnotReference
2925 = dynamic_cast<vcl::filter::PDFReferenceElement
*>(pAnnots
->GetElements()[0]);
2926 CPPUNIT_ASSERT(pAnnotReference
);
2927 vcl::filter::PDFObjectElement
* pAnnot
= pAnnotReference
->LookupObject();
2928 CPPUNIT_ASSERT(pAnnot
);
2929 // We're expecting something like /Type /Annot /A << /Type /Action /S /URI /URI (path)
2930 CPPUNIT_ASSERT_EQUAL(
2932 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
))->GetValue());
2933 CPPUNIT_ASSERT_EQUAL(
2935 static_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
))->GetValue());
2936 auto pAction
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pAnnot
->Lookup("A"_ostr
));
2937 CPPUNIT_ASSERT(pAction
);
2939 = dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(pAction
->LookupElement("URI"_ostr
));
2940 CPPUNIT_ASSERT(pURIElem
);
2942 CPPUNIT_ASSERT_EQUAL("https://google.com/"_ostr
, pURIElem
->GetValue());
2945 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf142806
)
2948 uno::Sequence
<beans::PropertyValue
> aFilterData(
2949 comphelper::InitPropertySequence({ { "PDFUACompliance", uno::Any(true) } }));
2950 aMediaDescriptor
[u
"FilterData"_ustr
] <<= aFilterData
;
2952 vcl::filter::PDFDocument aDocument
;
2953 load(u
"LinkPages.fodt", aDocument
);
2955 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
2956 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aPages
.size());
2958 vcl::filter::PDFObjectElement
* pDocument(nullptr);
2959 for (const auto& rDocElement
: aDocument
.GetElements())
2961 auto pObject1
= dynamic_cast<vcl::filter::PDFObjectElement
*>(rDocElement
.get());
2964 auto pType1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("Type"_ostr
));
2965 if (pType1
&& pType1
->GetValue() == "StructElem")
2967 auto pS1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObject1
->Lookup("S"_ostr
));
2968 if (pS1
&& pS1
->GetValue() == "Document")
2970 pDocument
= pObject1
;
2974 CPPUNIT_ASSERT(pDocument
);
2976 auto pKidsD
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pDocument
->Lookup("K"_ostr
));
2977 CPPUNIT_ASSERT(pKidsD
);
2978 // assume there are no MCID ref at this level
2979 auto pKidsDv
= pKidsD
->GetElements();
2980 auto pRefKidD0
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsDv
[0]);
2981 CPPUNIT_ASSERT(pRefKidD0
);
2982 auto pObjectD0
= pRefKidD0
->LookupObject();
2983 CPPUNIT_ASSERT(pObjectD0
);
2984 auto pTypeD0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD0
->Lookup("Type"_ostr
));
2985 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD0
->GetValue());
2986 auto pSD0
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD0
->Lookup("S"_ostr
));
2987 CPPUNIT_ASSERT_EQUAL("H1"_ostr
, pSD0
->GetValue());
2989 auto pKidsD0
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD0
->Lookup("K"_ostr
));
2990 CPPUNIT_ASSERT(pKidsD0
);
2991 auto pKidsD0v
= pKidsD0
->GetElements();
2993 auto pRefKidD00
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[0]);
2994 CPPUNIT_ASSERT(pRefKidD00
);
2995 auto pObjectD00
= pRefKidD00
->LookupObject();
2996 CPPUNIT_ASSERT(pObjectD00
);
2997 auto pTypeD00
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD00
->Lookup("Type"_ostr
));
2998 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD00
->GetValue());
2999 auto pSD00
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD00
->Lookup("S"_ostr
));
3000 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD00
->GetValue());
3002 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD00
->Lookup("K"_ostr
));
3005 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3007 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3008 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3017 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3018 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3019 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3020 pObjR
->LookupElement("Obj"_ostr
));
3021 auto pAnnot
= pAnnotRef
->LookupObject();
3023 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3024 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3026 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3027 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3028 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3029 pAnnot
->Lookup("Contents"_ostr
));
3030 CPPUNIT_ASSERT_EQUAL(
3031 u
"foo foo foo foo"_ustr
,
3032 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3033 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3034 pAnnot
->Lookup("StructParent"_ostr
));
3035 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3037 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3038 CPPUNIT_ASSERT(pARect
);
3039 const auto& rElements
= pARect
->GetElements();
3040 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3041 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3042 CPPUNIT_ASSERT(pNumL
);
3043 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.693, pNumL
->GetValue(), 1e-3);
3044 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3045 CPPUNIT_ASSERT(pNumT
);
3046 CPPUNIT_ASSERT_DOUBLES_EQUAL(240.455, pNumT
->GetValue(), 1e-3);
3047 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3048 CPPUNIT_ASSERT(pNumR
);
3049 CPPUNIT_ASSERT_DOUBLES_EQUAL(241.007, pNumR
->GetValue(), 1e-3);
3050 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3051 CPPUNIT_ASSERT(pNumB
);
3052 CPPUNIT_ASSERT_DOUBLES_EQUAL(350.855, pNumB
->GetValue(), 1e-3);
3055 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3056 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3059 auto pRefKidD01
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[1]);
3060 CPPUNIT_ASSERT(pRefKidD01
);
3061 auto pObjectD01
= pRefKidD01
->LookupObject();
3062 CPPUNIT_ASSERT(pObjectD01
);
3063 auto pTypeD01
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD01
->Lookup("Type"_ostr
));
3064 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD01
->GetValue());
3065 auto pSD01
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD01
->Lookup("S"_ostr
));
3066 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD01
->GetValue());
3068 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD01
->Lookup("K"_ostr
));
3071 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3073 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3074 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3083 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3084 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3085 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3086 pObjR
->LookupElement("Obj"_ostr
));
3087 auto pAnnot
= pAnnotRef
->LookupObject();
3089 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3090 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3092 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3093 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3094 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3095 pAnnot
->Lookup("Contents"_ostr
));
3096 CPPUNIT_ASSERT_EQUAL(
3097 u
"foo foo foo foo"_ustr
,
3098 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3099 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3100 pAnnot
->Lookup("StructParent"_ostr
));
3101 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3103 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3104 CPPUNIT_ASSERT(pARect
);
3105 const auto& rElements
= pARect
->GetElements();
3106 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3107 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3108 CPPUNIT_ASSERT(pNumL
);
3109 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.643, pNumL
->GetValue(), 1e-3);
3110 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3111 CPPUNIT_ASSERT(pNumT
);
3112 CPPUNIT_ASSERT_DOUBLES_EQUAL(130.055, pNumT
->GetValue(), 1e-3);
3113 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3114 CPPUNIT_ASSERT(pNumR
);
3115 CPPUNIT_ASSERT_DOUBLES_EQUAL(241.007, pNumR
->GetValue(), 1e-3);
3116 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3117 CPPUNIT_ASSERT(pNumB
);
3118 CPPUNIT_ASSERT_DOUBLES_EQUAL(240.455, pNumB
->GetValue(), 1e-3);
3121 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3122 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3125 auto pRefKidD02
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[2]);
3126 CPPUNIT_ASSERT(pRefKidD02
);
3127 auto pObjectD02
= pRefKidD02
->LookupObject();
3128 CPPUNIT_ASSERT(pObjectD02
);
3129 auto pTypeD02
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD02
->Lookup("Type"_ostr
));
3130 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD02
->GetValue());
3131 auto pSD02
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD02
->Lookup("S"_ostr
));
3132 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD02
->GetValue());
3134 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD02
->Lookup("K"_ostr
));
3137 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3139 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3140 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3149 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3150 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3151 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3152 pObjR
->LookupElement("Obj"_ostr
));
3153 auto pAnnot
= pAnnotRef
->LookupObject();
3155 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3156 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3158 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3159 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3160 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3161 pAnnot
->Lookup("Contents"_ostr
));
3162 CPPUNIT_ASSERT_EQUAL(
3163 u
"foo foo foo foo"_ustr
,
3164 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3165 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3166 pAnnot
->Lookup("StructParent"_ostr
));
3167 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3169 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3170 CPPUNIT_ASSERT(pARect
);
3171 const auto& rElements
= pARect
->GetElements();
3172 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3173 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3174 CPPUNIT_ASSERT(pNumL
);
3175 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.643, pNumL
->GetValue(), 1e-3);
3176 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3177 CPPUNIT_ASSERT(pNumT
);
3178 CPPUNIT_ASSERT_DOUBLES_EQUAL(252.455, pNumT
->GetValue(), 1e-3);
3179 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3180 CPPUNIT_ASSERT(pNumR
);
3181 CPPUNIT_ASSERT_DOUBLES_EQUAL(241.007, pNumR
->GetValue(), 1e-3);
3182 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3183 CPPUNIT_ASSERT(pNumB
);
3184 CPPUNIT_ASSERT_DOUBLES_EQUAL(362.855, pNumB
->GetValue(), 1e-3);
3187 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3188 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3191 auto pRefKidD03
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD0v
[3]);
3192 CPPUNIT_ASSERT(pRefKidD03
);
3193 auto pObjectD03
= pRefKidD03
->LookupObject();
3194 CPPUNIT_ASSERT(pObjectD03
);
3195 auto pTypeD03
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD03
->Lookup("Type"_ostr
));
3196 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD03
->GetValue());
3197 auto pSD03
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD03
->Lookup("S"_ostr
));
3198 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD03
->GetValue());
3200 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD03
->Lookup("K"_ostr
));
3203 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3205 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3206 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3215 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3216 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3217 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3218 pObjR
->LookupElement("Obj"_ostr
));
3219 auto pAnnot
= pAnnotRef
->LookupObject();
3221 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3222 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3224 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3225 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3226 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3227 pAnnot
->Lookup("Contents"_ostr
));
3228 CPPUNIT_ASSERT_EQUAL(
3229 u
"foo foo foo foo"_ustr
,
3230 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3231 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3232 pAnnot
->Lookup("StructParent"_ostr
));
3233 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3235 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3236 CPPUNIT_ASSERT(pARect
);
3237 const auto& rElements
= pARect
->GetElements();
3238 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3239 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3240 CPPUNIT_ASSERT(pNumL
);
3241 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.643, pNumL
->GetValue(), 1e-3);
3242 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3243 CPPUNIT_ASSERT(pNumT
);
3244 CPPUNIT_ASSERT_DOUBLES_EQUAL(142.055, pNumT
->GetValue(), 1e-3);
3245 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3246 CPPUNIT_ASSERT(pNumR
);
3247 CPPUNIT_ASSERT_DOUBLES_EQUAL(206.007, pNumR
->GetValue(), 1e-3);
3248 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3249 CPPUNIT_ASSERT(pNumB
);
3250 CPPUNIT_ASSERT_DOUBLES_EQUAL(252.455, pNumB
->GetValue(), 1e-3);
3253 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3254 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3256 auto pRefKidD1
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsDv
[1]);
3257 CPPUNIT_ASSERT(pRefKidD1
);
3258 auto pObjectD1
= pRefKidD1
->LookupObject();
3259 CPPUNIT_ASSERT(pObjectD1
);
3260 auto pTypeD1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD1
->Lookup("Type"_ostr
));
3261 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD1
->GetValue());
3262 auto pSD1
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD1
->Lookup("S"_ostr
));
3263 CPPUNIT_ASSERT_EQUAL("Text#20body"_ostr
, pSD1
->GetValue());
3265 auto pKidsD1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD1
->Lookup("K"_ostr
));
3266 CPPUNIT_ASSERT(pKidsD1
);
3267 auto pKidsD1v
= pKidsD1
->GetElements();
3269 auto pRefKidD10
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD1v
[0]);
3270 CPPUNIT_ASSERT(pRefKidD10
);
3271 auto pObjectD10
= pRefKidD10
->LookupObject();
3272 CPPUNIT_ASSERT(pObjectD10
);
3273 auto pTypeD10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD10
->Lookup("Type"_ostr
));
3274 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD10
->GetValue());
3275 auto pSD10
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD10
->Lookup("S"_ostr
));
3276 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD10
->GetValue());
3278 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD10
->Lookup("K"_ostr
));
3281 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3283 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3284 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3293 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3294 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3295 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3296 pObjR
->LookupElement("Obj"_ostr
));
3297 auto pAnnot
= pAnnotRef
->LookupObject();
3299 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3300 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3302 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3303 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3304 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3305 pAnnot
->Lookup("Contents"_ostr
));
3306 CPPUNIT_ASSERT_EQUAL(
3307 u
"foo foo foo foo"_ustr
,
3308 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3309 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3310 pAnnot
->Lookup("StructParent"_ostr
));
3311 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3313 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3314 CPPUNIT_ASSERT(pARect
);
3315 const auto& rElements
= pARect
->GetElements();
3316 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3317 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3318 CPPUNIT_ASSERT(pNumL
);
3319 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.693, pNumL
->GetValue(), 1e-3);
3320 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3321 CPPUNIT_ASSERT(pNumT
);
3322 CPPUNIT_ASSERT_DOUBLES_EQUAL(252.455, pNumT
->GetValue(), 1e-3);
3323 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3324 CPPUNIT_ASSERT(pNumR
);
3325 CPPUNIT_ASSERT_DOUBLES_EQUAL(241.007, pNumR
->GetValue(), 1e-3);
3326 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3327 CPPUNIT_ASSERT(pNumB
);
3328 CPPUNIT_ASSERT_DOUBLES_EQUAL(362.855, pNumB
->GetValue(), 1e-3);
3331 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3332 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3335 auto pRefKidD11
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD1v
[1]);
3336 CPPUNIT_ASSERT(pRefKidD11
);
3337 auto pObjectD11
= pRefKidD11
->LookupObject();
3338 CPPUNIT_ASSERT(pObjectD11
);
3339 auto pTypeD11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD11
->Lookup("Type"_ostr
));
3340 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD11
->GetValue());
3341 auto pSD11
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD11
->Lookup("S"_ostr
));
3342 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD11
->GetValue());
3344 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD11
->Lookup("K"_ostr
));
3347 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3349 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3350 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3359 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3360 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3361 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3362 pObjR
->LookupElement("Obj"_ostr
));
3363 auto pAnnot
= pAnnotRef
->LookupObject();
3365 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3366 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3368 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3369 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3370 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3371 pAnnot
->Lookup("Contents"_ostr
));
3372 CPPUNIT_ASSERT_EQUAL(
3373 u
"foo foo foo foo"_ustr
,
3374 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3375 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3376 pAnnot
->Lookup("StructParent"_ostr
));
3377 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3379 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3380 CPPUNIT_ASSERT(pARect
);
3381 const auto& rElements
= pARect
->GetElements();
3382 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3383 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3384 CPPUNIT_ASSERT(pNumL
);
3385 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.643, pNumL
->GetValue(), 1e-3);
3386 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3387 CPPUNIT_ASSERT(pNumT
);
3388 CPPUNIT_ASSERT_DOUBLES_EQUAL(140.005, pNumT
->GetValue(), 1e-3);
3389 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3390 CPPUNIT_ASSERT(pNumR
);
3391 CPPUNIT_ASSERT_DOUBLES_EQUAL(241.007, pNumR
->GetValue(), 1e-3);
3392 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3393 CPPUNIT_ASSERT(pNumB
);
3394 CPPUNIT_ASSERT_DOUBLES_EQUAL(252.455, pNumB
->GetValue(), 1e-3);
3397 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3398 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3401 auto pRefKidD12
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD1v
[2]);
3402 CPPUNIT_ASSERT(pRefKidD12
);
3403 auto pObjectD12
= pRefKidD12
->LookupObject();
3404 CPPUNIT_ASSERT(pObjectD12
);
3405 auto pTypeD12
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD12
->Lookup("Type"_ostr
));
3406 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD12
->GetValue());
3407 auto pSD12
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD12
->Lookup("S"_ostr
));
3408 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD12
->GetValue());
3410 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD12
->Lookup("K"_ostr
));
3413 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3415 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3416 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3425 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3426 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3427 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3428 pObjR
->LookupElement("Obj"_ostr
));
3429 auto pAnnot
= pAnnotRef
->LookupObject();
3431 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3432 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3434 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3435 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3436 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3437 pAnnot
->Lookup("Contents"_ostr
));
3438 CPPUNIT_ASSERT_EQUAL(
3439 u
"foo foo foo foo"_ustr
,
3440 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3441 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3442 pAnnot
->Lookup("StructParent"_ostr
));
3443 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3445 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3446 CPPUNIT_ASSERT(pARect
);
3447 const auto& rElements
= pARect
->GetElements();
3448 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3449 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3450 CPPUNIT_ASSERT(pNumL
);
3451 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.643, pNumL
->GetValue(), 1e-3);
3452 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3453 CPPUNIT_ASSERT(pNumT
);
3454 CPPUNIT_ASSERT_DOUBLES_EQUAL(252.455, pNumT
->GetValue(), 1e-3);
3455 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3456 CPPUNIT_ASSERT(pNumR
);
3457 CPPUNIT_ASSERT_DOUBLES_EQUAL(241.007, pNumR
->GetValue(), 1e-3);
3458 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3459 CPPUNIT_ASSERT(pNumB
);
3460 CPPUNIT_ASSERT_DOUBLES_EQUAL(362.855, pNumB
->GetValue(), 1e-3);
3463 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3464 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3467 auto pRefKidD13
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(pKidsD1v
[3]);
3468 CPPUNIT_ASSERT(pRefKidD13
);
3469 auto pObjectD13
= pRefKidD13
->LookupObject();
3470 CPPUNIT_ASSERT(pObjectD13
);
3471 auto pTypeD13
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD13
->Lookup("Type"_ostr
));
3472 CPPUNIT_ASSERT_EQUAL("StructElem"_ostr
, pTypeD13
->GetValue());
3473 auto pSD13
= dynamic_cast<vcl::filter::PDFNameElement
*>(pObjectD13
->Lookup("S"_ostr
));
3474 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pSD13
->GetValue());
3476 auto pKids
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pObjectD13
->Lookup("K"_ostr
));
3479 for (size_t i
= 0; i
< pKids
->GetElements().size(); ++i
)
3481 auto pNum
= dynamic_cast<vcl::filter::PDFNumberElement
*>(pKids
->GetElement(i
));
3482 auto pObjR
= dynamic_cast<vcl::filter::PDFDictionaryElement
*>(pKids
->GetElement(i
));
3491 = dynamic_cast<vcl::filter::PDFNameElement
*>(pObjR
->LookupElement("Type"_ostr
));
3492 CPPUNIT_ASSERT_EQUAL("OBJR"_ostr
, pOType
->GetValue());
3493 auto pAnnotRef
= dynamic_cast<vcl::filter::PDFReferenceElement
*>(
3494 pObjR
->LookupElement("Obj"_ostr
));
3495 auto pAnnot
= pAnnotRef
->LookupObject();
3497 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Type"_ostr
));
3498 CPPUNIT_ASSERT_EQUAL("Annot"_ostr
, pAType
->GetValue());
3500 = dynamic_cast<vcl::filter::PDFNameElement
*>(pAnnot
->Lookup("Subtype"_ostr
));
3501 CPPUNIT_ASSERT_EQUAL("Link"_ostr
, pASubtype
->GetValue());
3502 auto pAContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(
3503 pAnnot
->Lookup("Contents"_ostr
));
3504 CPPUNIT_ASSERT_EQUAL(
3505 u
"foo foo foo foo"_ustr
,
3506 ::vcl::filter::PDFDocument::DecodeHexStringUTF16BE(*pAContents
));
3507 auto pStructParent
= dynamic_cast<vcl::filter::PDFNumberElement
*>(
3508 pAnnot
->Lookup("StructParent"_ostr
));
3509 CPPUNIT_ASSERT(pStructParent
); // every link must have it!
3511 = dynamic_cast<vcl::filter::PDFArrayElement
*>(pAnnot
->Lookup("Rect"_ostr
));
3512 CPPUNIT_ASSERT(pARect
);
3513 const auto& rElements
= pARect
->GetElements();
3514 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), rElements
.size());
3515 const auto* pNumL
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[0]);
3516 CPPUNIT_ASSERT(pNumL
);
3517 CPPUNIT_ASSERT_DOUBLES_EQUAL(56.643, pNumL
->GetValue(), 1e-3);
3518 const auto* pNumT
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[1]);
3519 CPPUNIT_ASSERT(pNumT
);
3520 CPPUNIT_ASSERT_DOUBLES_EQUAL(140.005, pNumT
->GetValue(), 1e-3);
3521 const auto* pNumR
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[2]);
3522 CPPUNIT_ASSERT(pNumR
);
3523 CPPUNIT_ASSERT_DOUBLES_EQUAL(184.707, pNumR
->GetValue(), 1e-3);
3524 const auto* pNumB
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rElements
[3]);
3525 CPPUNIT_ASSERT(pNumB
);
3526 CPPUNIT_ASSERT_DOUBLES_EQUAL(252.455, pNumB
->GetValue(), 1e-3);
3529 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nMCID
)>(1), nMCID
);
3530 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nRef
)>(1), nRef
);
3533 // the problem was that the links in follow frames were all missing
3534 auto pAnnots0
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[0]->Lookup("Annots"_ostr
));
3535 CPPUNIT_ASSERT(pAnnots0
);
3536 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pAnnots0
->GetElements().size());
3537 auto pAnnots1
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[1]->Lookup("Annots"_ostr
));
3538 CPPUNIT_ASSERT(pAnnots1
);
3539 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pAnnots1
->GetElements().size());
3540 auto pAnnots2
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[2]->Lookup("Annots"_ostr
));
3541 CPPUNIT_ASSERT(pAnnots2
);
3542 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pAnnots2
->GetElements().size());
3543 auto pAnnots3
= dynamic_cast<vcl::filter::PDFArrayElement
*>(aPages
[3]->Lookup("Annots"_ostr
));
3544 CPPUNIT_ASSERT(pAnnots3
);
3545 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), pAnnots3
->GetElements().size());
3548 CPPUNIT_TEST_FIXTURE(PdfExportTest
, testTdf115967
)
3550 saveAsPDF(u
"tdf115967.odt");
3551 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
3552 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
3554 // Get the first page
3555 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
3556 CPPUNIT_ASSERT(pPdfPage
);
3557 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
3559 // Make sure the elements inside a formula in a RTL document are exported
3560 // LTR ( m=750abc ) and not RTL ( m=057cba )
3561 int nPageObjectCount
= pPdfPage
->getObjectCount();
3563 for (int i
= 0; i
< nPageObjectCount
; ++i
)
3565 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject
= pPdfPage
->getObject(i
);
3566 if (pPageObject
->getType() != vcl::pdf::PDFPageObjectType::Text
)
3568 OUString sChar
= pPageObject
->getText(pTextPage
);
3569 sText
+= o3tl::trim(sChar
);
3571 CPPUNIT_ASSERT_EQUAL(u
"m=750abc"_ustr
, sText
);
3574 } // end anonymous namespace
3576 CPPUNIT_PLUGIN_IMPLEMENT();
3578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */