Revert "tdf#158280 Replace usage of InputDialog with SvxNameDialog"
[LibreOffice.git] / sc / qa / extras / scpdfexport.cxx
blobbd0e8d21747b407fb7ca4c1b586e48c201323ae8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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>
22 #include <docsh.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>
30 #if USE_TLS_NSS
31 #include <nss.h>
32 #endif
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
43 public:
44 ScPDFExportTest();
45 ~ScPDFExportTest();
47 // helpers
48 private:
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);
58 // unit tests
59 public:
60 void testPopupRectangleSize_Tdf162955();
61 void testMediaShapeScreen_Tdf159094();
62 void testExportRange_Tdf120161();
63 void testExportFitToPage_Tdf103516();
64 void testUnoCommands_Tdf120161();
65 void testTdf64703_hiddenPageBreak();
66 void testTdf159068();
67 void testTdf159067();
68 void testTdf159066();
69 void testTdf159065();
70 void testTdf123870();
71 void testTdf143978();
72 void testTdf120190();
73 void testTdf84012();
74 void testTdf78897();
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()
104 #if USE_TLS_NSS
105 NSS_Shutdown();
106 #endif
109 bool ScPDFExportTest::hasTextInPdf(const char* sText, bool& bFound)
111 SvStream* pStream = maTempFile.GetStream(StreamMode::STD_READ);
112 CPPUNIT_ASSERT(pStream);
114 // get file size
115 const std::size_t nFileSize = pStream->TellEnd();
116 if (nFileSize == 0)
117 return false;
119 // read file content
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);
130 delete[] pBuffer;
132 // close and return the status
133 pStream = nullptr;
134 maTempFile.CloseStream();
135 return (nRead == nFileSize);
138 void ScPDFExportTest::exportToPDF(const uno::Reference<frame::XModel>& xModel, const ScRange& range)
140 // get XSpreadsheet
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())
176 // call storeToURL()
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,
206 EE_CHAR_FONTINFO);
207 aItemSet.Put(aItem);
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);
216 // A1:B8
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);
228 // A1
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);
240 // Popup annotation
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);
265 // 2. Setup data
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();
280 rEE.Clear();
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());
286 // A1:G1
288 ScRange range1(0, 0, 0, 6, 0, 0);
289 exportToPDF(xModel, range1);
290 bool bFound = false;
291 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
292 CPPUNIT_ASSERT_EQUAL(false, bFound);
295 // G1:H1
297 ScRange range1(6, 0, 0, 7, 0, 0);
298 exportToPDF(xModel, range1);
299 bool bFound = false;
300 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
301 CPPUNIT_ASSERT_EQUAL(true, bFound);
304 // H1:I1
306 ScRange range1(7, 0, 0, 8, 0, 0);
307 exportToPDF(xModel, range1);
308 bool bFound = false;
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);
325 // 2. Setup data
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);
343 bool bFound = false;
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);
352 bool bFound = false;
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(),
360 UNO_SET_THROW);
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;
367 sal_Int16 nScale;
368 aScaleX <<= static_cast<sal_Int16>(1);
369 xProp->setPropertyValue(u"ScaleToPagesX"_ustr, aScaleX);
370 aScaleX = xProp->getPropertyValue(u"ScaleToPagesX"_ustr);
371 aScaleX >>= nScale;
372 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), nScale);
374 aScaleY = xProp->getPropertyValue(u"ScaleToPagesY"_ustr);
375 aScaleY >>= nScale;
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);
382 bool bFound = false;
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);
391 bool bFound = false;
392 CPPUNIT_ASSERT(hasTextInPdf("/Count 1>>", bFound));
393 CPPUNIT_ASSERT_EQUAL(true, bFound);
397 void ScPDFExportTest::testUnoCommands_Tdf120161()
399 loadFromFile(u"tdf120161.ods");
401 // A1:G1
403 exportToPDFWithUnoCommands(u"A1:G1"_ustr);
404 bool bFound = false;
405 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
406 CPPUNIT_ASSERT_EQUAL(false, bFound);
409 // G1:H1
411 exportToPDFWithUnoCommands(u"G1:H1"_ustr);
412 bool bFound = false;
413 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
414 CPPUNIT_ASSERT_EQUAL(true, bFound);
417 // H1:I1
419 exportToPDFWithUnoCommands(u"H1:I1"_ustr);
420 bool bFound = false;
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);
436 bool bFound = false;
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);
447 // A1:C3
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();
465 // Uncompress it.
466 SvMemoryStream aUncompressed;
467 ZCodec aZCodec;
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();
476 auto nArtifact(0);
477 auto nLine(0);
478 while (true)
480 ++nLine;
481 auto const pLine = ::std::find(pStart, pEnd, '\n');
482 if (pLine == pEnd)
484 break;
486 std::string_view const line(pStart, pLine - pStart);
487 pStart = pLine + 1;
488 if (!line.empty() && line[0] != '%')
490 ::std::cerr << nLine << ": " << line << "\n ";
491 if (o3tl::starts_with(line, "/Artifact BMC"))
492 nArtifact++;
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);
507 // A1:B3
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();
525 // Uncompress it.
526 SvMemoryStream aUncompressed;
527 ZCodec aZCodec;
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();
536 auto nArtifact(0);
537 auto nLine(0);
538 while (true)
540 ++nLine;
541 auto const pLine = ::std::find(pStart, pEnd, '\n');
542 if (pLine == pEnd)
544 break;
546 std::string_view const line(pStart, pLine - pStart);
547 pStart = pLine + 1;
548 if (!line.empty() && line[0] != '%')
550 ::std::cerr << nLine << ": " << line << "\n ";
551 if (o3tl::starts_with(line, "/Artifact BMC"))
552 nArtifact++;
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);
567 // A1:E5
568 ScRange range1(0, 0, 0, 4, 4, 0);
569 exportToPDF(xModel, range1);
571 bool bFound = false;
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);
583 // A1:A3
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();
601 // Uncompress it.
602 SvMemoryStream aUncompressed;
603 ZCodec aZCodec;
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();
612 auto nLink(0);
613 auto nLine(0);
614 while (true)
616 ++nLine;
617 auto const pLine = ::std::find(pStart, pEnd, '\n');
618 if (pLine == pEnd)
620 break;
622 std::string_view const line(pStart, pLine - pStart);
623 pStart = pLine + 1;
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"))
628 nLink++;
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);
641 // A1:G4
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();
658 // Uncompress it.
659 SvMemoryStream aUncompressed;
660 ZCodec aZCodec;
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();
669 enum
671 Default,
672 Artifact,
673 Tagged
674 } state
675 = Default;
677 auto nLine(0);
678 auto nTagged(0);
679 auto nArtifacts(0);
680 while (true)
682 ++nLine;
683 auto const pLine = ::std::find(pStart, pEnd, '\n');
684 if (pLine == pEnd)
686 break;
688 std::string_view const line(pStart, pLine - pStart);
689 pStart = pLine + 1;
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);
696 state = Artifact;
697 ++nArtifacts;
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);
704 state = Tagged;
705 ++nTagged;
707 else if (line == "EMC")
709 CPPUNIT_ASSERT_MESSAGE("unexpected end", state != Default);
710 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();
727 if (!pPDFium)
729 return;
732 loadFromFile(u"tdf143978.ods");
733 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
735 // A1:A2
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
759 // - Actual : ###
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();
768 if (!pPDFium)
770 return;
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, {});
791 // A1
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
823 // - Expected: 5
824 // - Actual : 56
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();
835 if (!pPDFium)
837 return;
840 loadFromFile(u"tdf84012.ods");
841 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
843 // A1
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();
870 if (!pPDFium)
872 return;
875 loadFromFile(u"tdf78897.xls");
876 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
878 // C3:D3
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);
908 // A1:H81
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: */