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>
11 #include <config_oox.h>
13 #include <test/unoapi_test.hxx>
15 #include <com/sun/star/frame/XStorable.hpp>
16 #include <com/sun/star/lang/XComponent.hpp>
17 #include <com/sun/star/sheet/XSpreadsheet.hpp>
18 #include <com/sun/star/table/XCellRange.hpp>
19 #include <com/sun/star/view/XSelectionSupplier.hpp>
20 #include <comphelper/propertysequence.hxx>
21 #include <unotools/tempfile.hxx>
23 #include <editutil.hxx>
24 #include <editeng/eeitem.hxx>
25 #include <editeng/fontitem.hxx>
26 #include <comphelper/propertyvalue.hxx>
28 #include <vcl/filter/PDFiumLibrary.hxx>
33 #include <vcl/filter/pdfdocument.hxx>
34 #include <tools/zcodec.hxx>
35 #include <o3tl/string_view.hxx>
37 using namespace css::lang
;
38 using namespace ::com::sun::star
;
39 using namespace ::com::sun::star::uno
;
41 class ScPDFExportTest
: public UnoApiTest
49 void exportToPDF(const uno::Reference
<frame::XModel
>& xModel
, const ScRange
& range
);
51 void exportToPDFWithUnoCommands(const OUString
& rRange
);
53 bool hasTextInPdf(const char* sText
, bool& bFound
);
55 void setFont(ScFieldEditEngine
& rEE
, sal_Int32 nStart
, sal_Int32 nEnd
,
56 const OUString
& rFontName
);
60 void testPopupRectangleSize_Tdf162955();
61 void testMediaShapeScreen_Tdf159094();
62 void testExportRange_Tdf120161();
63 void testExportFitToPage_Tdf103516();
64 void testUnoCommands_Tdf120161();
65 void testTdf64703_hiddenPageBreak();
75 void testForcepoint97();
77 CPPUNIT_TEST_SUITE(ScPDFExportTest
);
78 CPPUNIT_TEST(testPopupRectangleSize_Tdf162955
);
79 CPPUNIT_TEST(testMediaShapeScreen_Tdf159094
);
80 CPPUNIT_TEST(testExportRange_Tdf120161
);
81 CPPUNIT_TEST(testExportFitToPage_Tdf103516
);
82 CPPUNIT_TEST(testUnoCommands_Tdf120161
);
83 CPPUNIT_TEST(testTdf64703_hiddenPageBreak
);
84 CPPUNIT_TEST(testTdf159068
);
85 CPPUNIT_TEST(testTdf159067
);
86 CPPUNIT_TEST(testTdf159066
);
87 CPPUNIT_TEST(testTdf159065
);
88 CPPUNIT_TEST(testTdf123870
);
89 CPPUNIT_TEST(testTdf143978
);
90 CPPUNIT_TEST(testTdf120190
);
91 CPPUNIT_TEST(testTdf84012
);
92 CPPUNIT_TEST(testTdf78897
);
93 CPPUNIT_TEST(testForcepoint97
);
94 CPPUNIT_TEST_SUITE_END();
97 ScPDFExportTest::ScPDFExportTest()
98 : UnoApiTest(u
"sc/qa/extras/testdocuments/"_ustr
)
102 ScPDFExportTest::~ScPDFExportTest()
109 bool ScPDFExportTest::hasTextInPdf(const char* sText
, bool& bFound
)
111 SvStream
* pStream
= maTempFile
.GetStream(StreamMode::STD_READ
);
112 CPPUNIT_ASSERT(pStream
);
115 const std::size_t nFileSize
= pStream
->TellEnd();
120 char* pBuffer
= new char[nFileSize
];
121 pStream
->Seek(STREAM_SEEK_TO_BEGIN
);
122 const std::size_t nRead
= pStream
->ReadBytes(pBuffer
, nFileSize
);
123 if (nRead
== nFileSize
)
125 const std::string
haystack(pBuffer
, pBuffer
+ nFileSize
);
126 const std::string
needle(sText
);
127 const std::size_t n
= haystack
.find(needle
);
128 bFound
= (n
!= std::string::npos
);
132 // close and return the status
134 maTempFile
.CloseStream();
135 return (nRead
== nFileSize
);
138 void ScPDFExportTest::exportToPDF(const uno::Reference
<frame::XModel
>& xModel
, const ScRange
& range
)
141 uno::Reference
<sheet::XSpreadsheetDocument
> xDoc(xModel
, uno::UNO_QUERY_THROW
);
142 uno::Reference
<sheet::XSpreadsheets
> xSheets(xDoc
->getSheets(), UNO_SET_THROW
);
143 uno::Reference
<container::XIndexAccess
> xIndex(xSheets
, uno::UNO_QUERY_THROW
);
144 uno::Reference
<sheet::XSpreadsheet
> rSheet(xIndex
->getByIndex(0), UNO_QUERY_THROW
);
146 // select requested cells to print
147 // query for the XCellRange interface
148 uno::Reference
<table::XCellRange
> xCellRange
= rSheet
->getCellRangeByPosition(
149 range
.aStart
.Col(), range
.aStart
.Row(), range
.aEnd
.Col(), range
.aEnd
.Row());
151 uno::Reference
<frame::XController
> xController
= xModel
->getCurrentController();
152 CPPUNIT_ASSERT(xController
.is());
154 uno::Reference
<view::XSelectionSupplier
> xSelection(xController
, uno::UNO_QUERY_THROW
);
155 CPPUNIT_ASSERT(xSelection
.is());
157 uno::Any
rCellRangeAny(xCellRange
);
158 xSelection
->select(rCellRangeAny
);
161 // init special pdf export params
162 css::uno::Sequence
<css::beans::PropertyValue
> aFilterData
{
163 comphelper::makePropertyValue(u
"Selection"_ustr
, xCellRange
),
164 comphelper::makePropertyValue(u
"Printing"_ustr
, sal_Int32(2)),
165 comphelper::makePropertyValue(u
"ViewPDFAfterExport"_ustr
, true),
166 comphelper::makePropertyValue(u
"PDFUACompliance"_ustr
, true)
169 // init set of params for storeToURL() call
170 css::uno::Sequence
<css::beans::PropertyValue
> seqArguments
{
171 comphelper::makePropertyValue(u
"FilterData"_ustr
, aFilterData
),
172 comphelper::makePropertyValue(u
"FilterName"_ustr
, u
"calc_pdf_Export"_ustr
),
173 comphelper::makePropertyValue(u
"URL"_ustr
, maTempFile
.GetURL())
177 uno::Reference
<lang::XComponent
> xComponent(mxComponent
, UNO_SET_THROW
);
178 uno::Reference
<css::frame::XStorable
> xStorable(xComponent
, UNO_QUERY
);
179 xStorable
->storeToURL(maTempFile
.GetURL(), seqArguments
);
182 void ScPDFExportTest::exportToPDFWithUnoCommands(const OUString
& rRange
)
184 uno::Sequence
<beans::PropertyValue
> aArgs
185 = comphelper::InitPropertySequence({ { "ToPoint", uno::Any(rRange
) } });
186 dispatchCommand(mxComponent
, u
".uno:GoToCell"_ustr
, aArgs
);
188 dispatchCommand(mxComponent
, u
".uno:DefinePrintArea"_ustr
, {});
190 uno::Sequence
<beans::PropertyValue
> aFilterData(comphelper::InitPropertySequence(
191 { { "ViewPDFAfterExport", uno::Any(true) }, { "Printing", uno::Any(sal_Int32(2)) } }));
193 uno::Sequence
<beans::PropertyValue
> aDescriptor(
194 comphelper::InitPropertySequence({ { "FilterName", uno::Any(u
"calc_pdf_Export"_ustr
) },
195 { "FilterData", uno::Any(aFilterData
) },
196 { "URL", uno::Any(maTempFile
.GetURL()) } }));
198 dispatchCommand(mxComponent
, u
".uno:ExportToPDF"_ustr
, aDescriptor
);
201 void ScPDFExportTest::setFont(ScFieldEditEngine
& rEE
, sal_Int32 nStart
, sal_Int32 nEnd
,
202 const OUString
& rFontName
)
204 SfxItemSet aItemSet
= rEE
.GetEmptyItemSet();
205 SvxFontItem
aItem(FAMILY_MODERN
, rFontName
, u
""_ustr
, PITCH_VARIABLE
, RTL_TEXTENCODING_UTF8
,
208 rEE
.QuickSetAttribs(aItemSet
, ESelection(0, nStart
, 0, nEnd
));
211 void ScPDFExportTest::testMediaShapeScreen_Tdf159094()
213 loadFromFile(u
"tdf159094.ods");
214 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
217 ScRange
aRange(0, 0, 0, 1, 7, 0);
219 // Without the fix, this test would crash on export media file to pdf
220 exportToPDF(xModel
, aRange
);
223 void ScPDFExportTest::testPopupRectangleSize_Tdf162955()
225 loadFromFile(u
"tdf162955_comment.ods");
226 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
229 ScRange
aRange(0, 0, 0, 0, 0, 0);
230 exportToPDF(xModel
, aRange
);
232 // Parse the export result with pdfium.
233 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
234 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
236 // Get the first page
237 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(0);
238 CPPUNIT_ASSERT(pPdfPage
);
242 auto pAnnotation
= pPdfPage
->getAnnotation(1);
243 CPPUNIT_ASSERT(pAnnotation
);
244 CPPUNIT_ASSERT_EQUAL(vcl::pdf::PDFAnnotationSubType::Popup
, pAnnotation
->getSubType());
245 CPPUNIT_ASSERT(!pAnnotation
->getRectangle().isEmpty());
246 double nWidth
= pAnnotation
->getRectangle().getWidth();
247 double nHeight
= pAnnotation
->getRectangle().getHeight();
248 CPPUNIT_ASSERT(nWidth
> 0);
249 CPPUNIT_ASSERT(nHeight
> 0);
253 // Selection was not taken into account during export into PDF
254 void ScPDFExportTest::testExportRange_Tdf120161()
256 // create test document
257 loadFromURL(u
"private:factory/scalc"_ustr
);
258 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
259 uno::Reference
<sheet::XSpreadsheetDocument
> xDoc(xModel
, uno::UNO_QUERY_THROW
);
260 uno::Reference
<sheet::XSpreadsheets
> xSheets(xDoc
->getSheets(), UNO_SET_THROW
);
261 uno::Reference
<container::XIndexAccess
> xIndex(xSheets
, uno::UNO_QUERY_THROW
);
262 xSheets
->insertNewByName(u
"First Sheet"_ustr
, 0);
263 uno::Reference
<sheet::XSpreadsheet
> rSheet(xIndex
->getByIndex(0), UNO_QUERY_THROW
);
267 SfxObjectShell
* pFoundShell
= SfxObjectShell::GetShellFromComponent(mxComponent
);
268 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell
);
269 ScDocShellRef xDocSh
= dynamic_cast<ScDocShell
*>(pFoundShell
);
270 CPPUNIT_ASSERT(xDocSh
);
272 // put some content into the first row with default font
273 ScDocument
& rDoc
= xDocSh
->GetDocument();
274 for (unsigned int r
= 0; r
< 1; ++r
)
275 for (unsigned int c
= 0; c
< 14; ++c
)
276 rDoc
.SetValue(ScAddress(c
, r
, 0), (r
+ 1) * (c
+ 1));
278 // set "Text" to H1 cell with "DejaVuSans" font
279 ScFieldEditEngine
& rEE
= rDoc
.GetEditEngine();
281 rEE
.SetTextCurrentDefaults(u
"Text"_ustr
);
282 setFont(rEE
, 0, 4, u
"DejaVuSans"_ustr
); // set font for first 4 chars
283 rDoc
.SetEditText(ScAddress(7, 0, 0), rEE
.CreateTextObject());
288 ScRange
range1(0, 0, 0, 6, 0, 0);
289 exportToPDF(xModel
, range1
);
291 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound
));
292 CPPUNIT_ASSERT_EQUAL(false, bFound
);
297 ScRange
range1(6, 0, 0, 7, 0, 0);
298 exportToPDF(xModel
, range1
);
300 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound
));
301 CPPUNIT_ASSERT_EQUAL(true, bFound
);
306 ScRange
range1(7, 0, 0, 8, 0, 0);
307 exportToPDF(xModel
, range1
);
309 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound
));
310 CPPUNIT_ASSERT_EQUAL(true, bFound
);
314 void ScPDFExportTest::testExportFitToPage_Tdf103516()
316 // create test document
317 loadFromURL(u
"private:factory/scalc"_ustr
);
318 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
319 uno::Reference
<sheet::XSpreadsheetDocument
> xDoc(xModel
, uno::UNO_QUERY_THROW
);
320 uno::Reference
<sheet::XSpreadsheets
> xSheets(xDoc
->getSheets(), UNO_SET_THROW
);
321 uno::Reference
<container::XIndexAccess
> xIndex(xSheets
, uno::UNO_QUERY_THROW
);
322 xSheets
->insertNewByName(u
"First Sheet"_ustr
, 0);
323 uno::Reference
<sheet::XSpreadsheet
> rSheet(xIndex
->getByIndex(0), UNO_QUERY_THROW
);
327 SfxObjectShell
* pFoundShell
= SfxObjectShell::GetShellFromComponent(mxComponent
);
328 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell
);
329 ScDocShellRef xDocSh
= dynamic_cast<ScDocShell
*>(pFoundShell
);
330 CPPUNIT_ASSERT(xDocSh
);
332 // put some content into the table
333 ScDocument
& rDoc
= xDocSh
->GetDocument();
334 for (unsigned int r
= 0; r
< 80; ++r
)
335 for (unsigned int c
= 0; c
< 12; ++c
)
336 rDoc
.SetValue(ScAddress(c
, r
, 0), (r
+ 1) * (c
+ 1));
339 // A1:G50: 2-page export
341 ScRange
range1(0, 0, 0, 6, 49, 0);
342 exportToPDF(xModel
, range1
);
344 CPPUNIT_ASSERT(hasTextInPdf("/Count 2>>", bFound
));
345 CPPUNIT_ASSERT_EQUAL(true, bFound
);
348 // A1:L80: 4-page export
350 ScRange
range1(0, 0, 0, 11, 79, 0);
351 exportToPDF(xModel
, range1
);
353 CPPUNIT_ASSERT(hasTextInPdf("/Count 4>>", bFound
));
354 CPPUNIT_ASSERT_EQUAL(true, bFound
);
357 // set fit to page: width=1 page, height=0 (automatic)
358 uno::Reference
<style::XStyleFamiliesSupplier
> xStyleFamSupp(xDoc
, UNO_QUERY_THROW
);
359 uno::Reference
<container::XNameAccess
> xStyleFamiliesNames(xStyleFamSupp
->getStyleFamilies(),
361 uno::Reference
<container::XNameAccess
> xPageStyles(
362 xStyleFamiliesNames
->getByName(u
"PageStyles"_ustr
), UNO_QUERY_THROW
);
363 uno::Any aDefaultStyle
= xPageStyles
->getByName(u
"Default"_ustr
);
364 uno::Reference
<beans::XPropertySet
> xProp(aDefaultStyle
, UNO_QUERY_THROW
);
366 uno::Any aScaleX
, aScaleY
;
368 aScaleX
<<= static_cast<sal_Int16
>(1);
369 xProp
->setPropertyValue(u
"ScaleToPagesX"_ustr
, aScaleX
);
370 aScaleX
= xProp
->getPropertyValue(u
"ScaleToPagesX"_ustr
);
372 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), nScale
);
374 aScaleY
= xProp
->getPropertyValue(u
"ScaleToPagesY"_ustr
);
376 CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nScale
);
378 // A1:G50 with fit to page width=1: slightly smaller zoom results only 1-page export
380 ScRange
range1(0, 0, 0, 6, 49, 0);
381 exportToPDF(xModel
, range1
);
383 CPPUNIT_ASSERT(hasTextInPdf("/Count 1>>", bFound
));
384 CPPUNIT_ASSERT_EQUAL(true, bFound
);
387 // A1:L80 with fit to page width=1: slightly smaller zoom results only 1-page export
389 ScRange
range1(0, 0, 0, 11, 79, 0);
390 exportToPDF(xModel
, range1
);
392 CPPUNIT_ASSERT(hasTextInPdf("/Count 1>>", bFound
));
393 CPPUNIT_ASSERT_EQUAL(true, bFound
);
397 void ScPDFExportTest::testUnoCommands_Tdf120161()
399 loadFromFile(u
"tdf120161.ods");
403 exportToPDFWithUnoCommands(u
"A1:G1"_ustr
);
405 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound
));
406 CPPUNIT_ASSERT_EQUAL(false, bFound
);
411 exportToPDFWithUnoCommands(u
"G1:H1"_ustr
);
413 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound
));
414 CPPUNIT_ASSERT_EQUAL(true, bFound
);
419 exportToPDFWithUnoCommands(u
"H1:I1"_ustr
);
421 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound
));
422 CPPUNIT_ASSERT_EQUAL(true, bFound
);
426 void ScPDFExportTest::testTdf64703_hiddenPageBreak()
428 loadFromFile(u
"tdf64703_hiddenPageBreak.ods");
430 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
432 // A1:A11: 4-page export
434 ScRange
range1(0, 0, 0, 0, 10, 0);
435 exportToPDF(xModel
, range1
);
437 CPPUNIT_ASSERT(hasTextInPdf("/Count 4>>", bFound
));
438 CPPUNIT_ASSERT_EQUAL(true, bFound
);
442 void ScPDFExportTest::testTdf159068()
444 loadFromFile(u
"tdf159068.ods");
445 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
448 ScRange
range1(0, 0, 0, 2, 2, 0);
449 exportToPDF(xModel
, range1
);
451 vcl::filter::PDFDocument aDocument
;
452 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
453 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
455 // The document has one page.
456 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
457 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
459 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
460 CPPUNIT_ASSERT(pContents
);
461 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
462 CPPUNIT_ASSERT(pStream
);
464 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
466 SvMemoryStream aUncompressed
;
468 aZCodec
.BeginCompression();
469 rObjectStream
.Seek(0);
470 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
471 CPPUNIT_ASSERT(aZCodec
.EndCompression());
473 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
474 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
481 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
486 std::string_view
const line(pStart
, pLine
- pStart
);
488 if (!line
.empty() && line
[0] != '%')
490 ::std::cerr
<< nLine
<< ": " << line
<< "\n ";
491 if (o3tl::starts_with(line
, "/Artifact BMC"))
496 // Without the fix in place, this test would have failed with
497 // - Expected: 5 (Artifact: Header, Footer, Rectangle, DetectiveArrow, ValidationCircle)
498 // - Actual : 2 (Artifact: Header, Footer)
499 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nArtifact
)>(5), nArtifact
);
502 void ScPDFExportTest::testTdf159067()
504 loadFromFile(u
"tdf159067.ods");
505 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
508 ScRange
range1(0, 0, 0, 1, 2, 0);
509 exportToPDF(xModel
, range1
);
511 vcl::filter::PDFDocument aDocument
;
512 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
513 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
515 // The document has one page.
516 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
517 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
519 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
520 CPPUNIT_ASSERT(pContents
);
521 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
522 CPPUNIT_ASSERT(pStream
);
524 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
526 SvMemoryStream aUncompressed
;
528 aZCodec
.BeginCompression();
529 rObjectStream
.Seek(0);
530 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
531 CPPUNIT_ASSERT(aZCodec
.EndCompression());
533 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
534 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
541 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
546 std::string_view
const line(pStart
, pLine
- pStart
);
548 if (!line
.empty() && line
[0] != '%')
550 ::std::cerr
<< nLine
<< ": " << line
<< "\n ";
551 if (o3tl::starts_with(line
, "/Artifact BMC"))
556 // Without the fix in place, this test would have failed with
557 // - Expected: 3 (Artifact: Header, Footer, TextBox)
558 // - Actual : 2 (Artifact: Header, Footer)
559 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nArtifact
)>(3), nArtifact
);
562 void ScPDFExportTest::testTdf159066()
564 loadFromFile(u
"tdf159066.ods");
565 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
568 ScRange
range1(0, 0, 0, 4, 4, 0);
569 exportToPDF(xModel
, range1
);
572 CPPUNIT_ASSERT(hasTextInPdf("/Alt<", bFound
));
574 // The OLE object contains alternative text description
575 CPPUNIT_ASSERT_EQUAL(true, bFound
);
578 void ScPDFExportTest::testTdf159065()
580 loadFromFile(u
"tdf159065.ods");
581 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
584 ScRange
range1(0, 0, 0, 0, 2, 0);
585 exportToPDF(xModel
, range1
);
587 vcl::filter::PDFDocument aDocument
;
588 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
589 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
591 // The document has one page.
592 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
593 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
595 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
596 CPPUNIT_ASSERT(pContents
);
597 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
598 CPPUNIT_ASSERT(pStream
);
600 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
602 SvMemoryStream aUncompressed
;
604 aZCodec
.BeginCompression();
605 rObjectStream
.Seek(0);
606 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
607 CPPUNIT_ASSERT(aZCodec
.EndCompression());
609 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
610 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
617 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
622 std::string_view
const line(pStart
, pLine
- pStart
);
624 if (!line
.empty() && line
[0] != '%')
626 ::std::cerr
<< nLine
<< ": " << line
<< "\n ";
627 if (o3tl::starts_with(line
, "/Link<</MCID") && o3tl::ends_with(line
, ">>BDC"))
632 // The tagged PDF file have to contains two link annotation
633 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nLink
)>(2), nLink
);
636 void ScPDFExportTest::testTdf123870()
638 loadFromFile(u
"tdf123870.ods");
639 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
642 ScRange
range1(0, 0, 0, 6, 4, 0);
643 exportToPDF(xModel
, range1
);
645 vcl::filter::PDFDocument aDocument
;
646 SvFileStream
aStream(maTempFile
.GetURL(), StreamMode::READ
);
647 CPPUNIT_ASSERT(aDocument
.Read(aStream
));
649 // The document has one page.
650 std::vector
<vcl::filter::PDFObjectElement
*> aPages
= aDocument
.GetPages();
651 CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPages
.size());
653 vcl::filter::PDFObjectElement
* pContents
= aPages
[0]->LookupObject("Contents"_ostr
);
654 CPPUNIT_ASSERT(pContents
);
655 vcl::filter::PDFStreamElement
* pStream
= pContents
->GetStream();
656 CPPUNIT_ASSERT(pStream
);
657 SvMemoryStream
& rObjectStream
= pStream
->GetMemory();
659 SvMemoryStream aUncompressed
;
661 aZCodec
.BeginCompression();
662 rObjectStream
.Seek(0);
663 aZCodec
.Decompress(rObjectStream
, aUncompressed
);
664 CPPUNIT_ASSERT(aZCodec
.EndCompression());
666 auto pStart
= static_cast<const char*>(aUncompressed
.GetData());
667 const char* const pEnd
= pStart
+ aUncompressed
.GetSize();
683 auto const pLine
= ::std::find(pStart
, pEnd
, '\n');
688 std::string_view
const line(pStart
, pLine
- pStart
);
690 if (!line
.empty() && line
[0] != '%')
692 ::std::cerr
<< nLine
<< ": " << line
<< "\n ";
693 if (o3tl::ends_with(line
, "/Artifact BMC"))
695 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
699 else if ((o3tl::starts_with(line
, "/P<</MCID") && o3tl::ends_with(line
, ">>BDC"))
700 || (o3tl::starts_with(line
, "/Figure<</MCID")
701 && o3tl::ends_with(line
, ">>BDC")))
703 CPPUNIT_ASSERT_EQUAL_MESSAGE("unexpected nesting", Default
, state
);
707 else if (line
== "EMC")
709 CPPUNIT_ASSERT_MESSAGE("unexpected end", state
!= Default
);
712 else if (nLine
> 1) // first line is expected "0.1 w"
714 CPPUNIT_ASSERT_MESSAGE("unexpected content outside MCS", state
!= Default
);
718 // text in cell + 1 shape
719 CPPUNIT_ASSERT_EQUAL(static_cast<decltype(nTagged
)>(9), nTagged
);
720 // header, footer, background color, color scale, shadow, cell border
721 CPPUNIT_ASSERT(nArtifacts
>= 6);
724 void ScPDFExportTest::testTdf143978()
726 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
732 loadFromFile(u
"tdf143978.ods");
733 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
736 ScRange
range1(0, 0, 0, 0, 1, 0);
737 exportToPDF(xModel
, range1
);
738 // Parse the export result with pdfium.
739 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
740 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
742 // Get the first page
743 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
744 CPPUNIT_ASSERT(pPdfPage
);
745 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
747 int nPageObjectCount
= pPdfPage
->getObjectCount();
748 CPPUNIT_ASSERT_EQUAL(2, nPageObjectCount
);
750 // Without the fix in place, this test would have failed with
751 // - Expected: Dies ist viel zu viel Text
752 // - Actual : Dies ist vie
753 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject1
= pPdfPage
->getObject(0);
754 OUString sText1
= pPageObject1
->getText(pTextPage
);
755 CPPUNIT_ASSERT_EQUAL(u
"Dies ist viel zu viel Text"_ustr
, sText1
);
757 // and it would also have failed with
758 // - Expected: 2021-11-17
760 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject2
= pPdfPage
->getObject(1);
761 OUString sText2
= pPageObject2
->getText(pTextPage
);
762 CPPUNIT_ASSERT_EQUAL(u
"2021-11-17"_ustr
, sText2
);
765 void ScPDFExportTest::testTdf120190()
767 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
773 loadFromURL(u
"private:factory/scalc"_ustr
);
774 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
776 uno::Reference
<sheet::XSpreadsheetDocument
> xDoc(mxComponent
, uno::UNO_QUERY_THROW
);
777 CPPUNIT_ASSERT_MESSAGE("no calc document", xDoc
.is());
779 uno::Reference
<sheet::XSpreadsheets
> xSheets(xDoc
->getSheets(), uno::UNO_SET_THROW
);
780 uno::Reference
<container::XIndexAccess
> xIA(xSheets
, uno::UNO_QUERY_THROW
);
781 uno::Reference
<sheet::XSpreadsheet
> xSheet0(xIA
->getByIndex(0), uno::UNO_QUERY_THROW
);
783 xSheet0
->getCellByPosition(0, 0)->setFormula(u
"=5&CHAR(10)&6"_ustr
);
785 uno::Sequence
<beans::PropertyValue
> aArgs
786 = comphelper::InitPropertySequence({ { "ToPoint", uno::Any(u
"A1"_ustr
) } });
787 dispatchCommand(mxComponent
, u
".uno:GoToCell"_ustr
, aArgs
);
789 dispatchCommand(mxComponent
, u
".uno:ConvertFormulaToValue"_ustr
, {});
792 ScRange
range1(0, 0, 0, 0, 0, 0);
793 exportToPDF(xModel
, range1
);
795 // Parse the export result with pdfium.
796 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
797 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
799 // Get the first page
800 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
801 CPPUNIT_ASSERT(pPdfPage
);
802 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
804 int nPageObjectCount
= pPdfPage
->getObjectCount();
805 CPPUNIT_ASSERT_EQUAL(5, nPageObjectCount
);
807 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject1
= pPdfPage
->getObject(0);
808 OUString sText1
= pPageObject1
->getText(pTextPage
);
809 CPPUNIT_ASSERT_EQUAL(u
"Sheet1"_ustr
, sText1
);
811 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject2
= pPdfPage
->getObject(1);
812 OUString sText2
= pPageObject2
->getText(pTextPage
);
813 CPPUNIT_ASSERT_EQUAL(u
"Page "_ustr
, sText2
);
815 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject3
= pPdfPage
->getObject(2);
816 OUString sText3
= pPageObject3
->getText(pTextPage
);
817 CPPUNIT_ASSERT_EQUAL(u
"1"_ustr
, sText3
);
819 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject4
= pPdfPage
->getObject(3);
820 OUString sText4
= pPageObject4
->getText(pTextPage
);
822 // Without the fix in place, this test would have failed with
825 CPPUNIT_ASSERT_EQUAL(u
"5"_ustr
, sText4
);
827 std::unique_ptr
<vcl::pdf::PDFiumPageObject
> pPageObject5
= pPdfPage
->getObject(4);
828 OUString sText5
= pPageObject5
->getText(pTextPage
);
829 CPPUNIT_ASSERT_EQUAL(u
"6"_ustr
, sText5
);
832 void ScPDFExportTest::testTdf84012()
834 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
840 loadFromFile(u
"tdf84012.ods");
841 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
844 ScRange
range1(0, 0, 0, 0, 0, 0);
845 exportToPDF(xModel
, range1
);
846 // Parse the export result with pdfium.
847 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
848 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
850 // Get the first page
851 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
852 CPPUNIT_ASSERT(pPdfPage
);
853 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
855 int nChars
= pTextPage
->countChars();
856 std::vector
<sal_uInt32
> aChars(nChars
);
857 for (int i
= 0; i
< nChars
; i
++)
858 aChars
[i
] = pTextPage
->getUnicode(i
);
859 OUString
aActualText(aChars
.data(), aChars
.size());
861 // Without the fix in place, this test would have failed with
862 // - Expected: Blah blah (blah, blah)
863 // - Actual : Blah blah
864 CPPUNIT_ASSERT_EQUAL(u
"Blah blah (blah, blah)"_ustr
, aActualText
);
867 void ScPDFExportTest::testTdf78897()
869 std::shared_ptr
<vcl::pdf::PDFium
> pPDFium
= vcl::pdf::PDFiumLibrary::get();
875 loadFromFile(u
"tdf78897.xls");
876 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
879 ScRange
range1(2, 2, 0, 3, 2, 0);
880 exportToPDF(xModel
, range1
);
881 // Parse the export result with pdfium.
882 std::unique_ptr
<vcl::pdf::PDFiumDocument
> pPdfDocument
= parsePDFExport();
883 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument
->getPageCount());
885 // Get the first page
886 std::unique_ptr
<vcl::pdf::PDFiumPage
> pPdfPage
= pPdfDocument
->openPage(/*nIndex=*/0);
887 CPPUNIT_ASSERT(pPdfPage
);
888 std::unique_ptr
<vcl::pdf::PDFiumTextPage
> pTextPage
= pPdfPage
->getTextPage();
890 int nChars
= pTextPage
->countChars();
891 std::vector
<sal_uInt32
> aChars(nChars
);
892 for (int i
= 0; i
< nChars
; i
++)
893 aChars
[i
] = pTextPage
->getUnicode(i
);
894 OUString
aActualText(aChars
.data(), aChars
.size());
896 // Without the fix in place, this test would have failed with
897 // - Expected: 11.00 11.00
898 // - Actual : 11.00 ###
899 CPPUNIT_ASSERT_EQUAL(u
" 11.00 11.00 "_ustr
, aActualText
);
902 // just needs to not crash on export to pdf
903 void ScPDFExportTest::testForcepoint97()
905 loadFromFile(u
"forcepoint97.xlsx");
906 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
909 ScRange
range1(0, 0, 0, 7, 81, 0);
910 exportToPDF(xModel
, range1
);
913 CPPUNIT_TEST_SUITE_REGISTRATION(ScPDFExportTest
);
914 CPPUNIT_PLUGIN_IMPLEMENT();
916 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */