calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / sc / qa / extras / scpdfexport.cxx
bloba3056965c7335e033d7cecd77d82ed0853246b1e
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/Desktop.hpp>
16 #include <com/sun/star/frame/XStorable.hpp>
17 #include <com/sun/star/lang/XComponent.hpp>
18 #include <com/sun/star/sheet/XSpreadsheet.hpp>
19 #include <com/sun/star/table/XCellRange.hpp>
20 #include <com/sun/star/view/XSelectionSupplier.hpp>
21 #include <comphelper/propertysequence.hxx>
22 #include <unotools/tempfile.hxx>
23 #include <docsh.hxx>
24 #include <editutil.hxx>
25 #include <editeng/eeitem.hxx>
26 #include <editeng/fontitem.hxx>
27 #include <osl/file.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <comphelper/propertyvalue.hxx>
31 #include <vcl/filter/PDFiumLibrary.hxx>
33 #if USE_TLS_NSS
34 #include <nss.h>
35 #endif
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 testExportRange_Tdf120161();
61 void testExportFitToPage_Tdf103516();
62 void testUnoCommands_Tdf120161();
63 void testTdf64703_hiddenPageBreak();
64 void testTdf143978();
65 void testTdf84012();
66 void testTdf78897();
67 void testForcepoint97();
69 CPPUNIT_TEST_SUITE(ScPDFExportTest);
70 CPPUNIT_TEST(testExportRange_Tdf120161);
71 CPPUNIT_TEST(testExportFitToPage_Tdf103516);
72 CPPUNIT_TEST(testUnoCommands_Tdf120161);
73 CPPUNIT_TEST(testTdf64703_hiddenPageBreak);
74 CPPUNIT_TEST(testTdf143978);
75 CPPUNIT_TEST(testTdf84012);
76 CPPUNIT_TEST(testTdf78897);
77 CPPUNIT_TEST(testForcepoint97);
78 CPPUNIT_TEST_SUITE_END();
81 ScPDFExportTest::ScPDFExportTest()
82 : UnoApiTest("sc/qa/extras/testdocuments/")
86 ScPDFExportTest::~ScPDFExportTest()
88 #if USE_TLS_NSS
89 NSS_Shutdown();
90 #endif
93 bool ScPDFExportTest::hasTextInPdf(const char* sText, bool& bFound)
95 SvStream* pStream = maTempFile.GetStream(StreamMode::STD_READ);
96 CPPUNIT_ASSERT(pStream);
98 // get file size
99 const std::size_t nFileSize = pStream->TellEnd();
100 if (nFileSize == 0)
101 return false;
103 // read file content
104 char* pBuffer = new char[nFileSize];
105 pStream->Seek(STREAM_SEEK_TO_BEGIN);
106 const std::size_t nRead = pStream->ReadBytes(pBuffer, nFileSize);
107 if (nRead == nFileSize)
109 const std::string haystack(pBuffer, pBuffer + nFileSize);
110 const std::string needle(sText);
111 const std::size_t n = haystack.find(needle);
112 bFound = (n != std::string::npos);
114 delete[] pBuffer;
116 // close and return the status
117 pStream = nullptr;
118 maTempFile.CloseStream();
119 return (nRead == nFileSize);
122 void ScPDFExportTest::exportToPDF(const uno::Reference<frame::XModel>& xModel, const ScRange& range)
124 // get XSpreadsheet
125 uno::Reference<sheet::XSpreadsheetDocument> xDoc(xModel, uno::UNO_QUERY_THROW);
126 uno::Reference<sheet::XSpreadsheets> xSheets(xDoc->getSheets(), UNO_SET_THROW);
127 uno::Reference<container::XIndexAccess> xIndex(xSheets, uno::UNO_QUERY_THROW);
128 uno::Reference<sheet::XSpreadsheet> rSheet(xIndex->getByIndex(0), UNO_QUERY_THROW);
130 // select requested cells to print
131 // query for the XCellRange interface
132 uno::Reference<table::XCellRange> xCellRange = rSheet->getCellRangeByPosition(
133 range.aStart.Col(), range.aStart.Row(), range.aEnd.Col(), range.aEnd.Row());
135 uno::Reference<frame::XController> xController = xModel->getCurrentController();
136 CPPUNIT_ASSERT(xController.is());
138 uno::Reference<view::XSelectionSupplier> xSelection(xController, uno::UNO_QUERY_THROW);
139 CPPUNIT_ASSERT(xSelection.is());
141 uno::Any rCellRangeAny(xCellRange);
142 xSelection->select(rCellRangeAny);
145 // init special pdf export params
146 css::uno::Sequence<css::beans::PropertyValue> aFilterData{
147 comphelper::makePropertyValue("Selection", xCellRange),
148 comphelper::makePropertyValue("Printing", sal_Int32(2)),
149 comphelper::makePropertyValue("ViewPDFAfterExport", true)
152 // init set of params for storeToURL() call
153 css::uno::Sequence<css::beans::PropertyValue> seqArguments{
154 comphelper::makePropertyValue("FilterData", aFilterData),
155 comphelper::makePropertyValue("FilterName", OUString("calc_pdf_Export")),
156 comphelper::makePropertyValue("URL", maTempFile.GetURL())
159 // call storeToURL()
160 uno::Reference<lang::XComponent> xComponent(mxComponent, UNO_SET_THROW);
161 uno::Reference<css::frame::XStorable> xStorable(xComponent, UNO_QUERY);
162 xStorable->storeToURL(maTempFile.GetURL(), seqArguments);
165 void ScPDFExportTest::exportToPDFWithUnoCommands(const OUString& rRange)
167 uno::Sequence<beans::PropertyValue> aArgs
168 = comphelper::InitPropertySequence({ { "ToPoint", uno::Any(rRange) } });
169 dispatchCommand(mxComponent, ".uno:GoToCell", aArgs);
171 dispatchCommand(mxComponent, ".uno:DefinePrintArea", {});
173 uno::Sequence<beans::PropertyValue> aFilterData(comphelper::InitPropertySequence(
174 { { "ViewPDFAfterExport", uno::Any(true) }, { "Printing", uno::Any(sal_Int32(2)) } }));
176 uno::Sequence<beans::PropertyValue> aDescriptor(
177 comphelper::InitPropertySequence({ { "FilterName", uno::Any(OUString("calc_pdf_Export")) },
178 { "FilterData", uno::Any(aFilterData) },
179 { "URL", uno::Any(maTempFile.GetURL()) } }));
181 dispatchCommand(mxComponent, ".uno:ExportToPDF", aDescriptor);
184 void ScPDFExportTest::setFont(ScFieldEditEngine& rEE, sal_Int32 nStart, sal_Int32 nEnd,
185 const OUString& rFontName)
187 ESelection aSel;
188 aSel.nStartPara = aSel.nEndPara = 0;
189 aSel.nStartPos = nStart;
190 aSel.nEndPos = nEnd;
192 SfxItemSet aItemSet = rEE.GetEmptyItemSet();
193 SvxFontItem aItem(FAMILY_MODERN, rFontName, "", PITCH_VARIABLE, RTL_TEXTENCODING_UTF8,
194 EE_CHAR_FONTINFO);
195 aItemSet.Put(aItem);
196 rEE.QuickSetAttribs(aItemSet, aSel);
199 // Selection was not taken into account during export into PDF
200 void ScPDFExportTest::testExportRange_Tdf120161()
202 // create test document
203 mxComponent = loadFromDesktop("private:factory/scalc");
204 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
205 uno::Reference<sheet::XSpreadsheetDocument> xDoc(xModel, uno::UNO_QUERY_THROW);
206 uno::Reference<sheet::XSpreadsheets> xSheets(xDoc->getSheets(), UNO_SET_THROW);
207 uno::Reference<container::XIndexAccess> xIndex(xSheets, uno::UNO_QUERY_THROW);
208 xSheets->insertNewByName("First Sheet", 0);
209 uno::Reference<sheet::XSpreadsheet> rSheet(xIndex->getByIndex(0), UNO_QUERY_THROW);
211 // 2. Setup data
213 SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(mxComponent);
214 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
215 ScDocShellRef xDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
216 CPPUNIT_ASSERT(xDocSh);
218 // put some content into the first row with default font
219 ScDocument& rDoc = xDocSh->GetDocument();
220 for (unsigned int r = 0; r < 1; ++r)
221 for (unsigned int c = 0; c < 14; ++c)
222 rDoc.SetValue(ScAddress(c, r, 0), (r + 1) * (c + 1));
224 // set "Text" to H1 cell with "DejaVuSans" font
225 ScFieldEditEngine& rEE = rDoc.GetEditEngine();
226 rEE.Clear();
227 rEE.SetTextCurrentDefaults("Text");
228 setFont(rEE, 0, 4, "DejaVuSans"); // set font for first 4 chars
229 rDoc.SetEditText(ScAddress(7, 0, 0), rEE.CreateTextObject());
232 // A1:G1
234 ScRange range1(0, 0, 0, 6, 0, 0);
235 exportToPDF(xModel, range1);
236 bool bFound = false;
237 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
238 CPPUNIT_ASSERT_EQUAL(false, bFound);
241 // G1:H1
243 ScRange range1(6, 0, 0, 7, 0, 0);
244 exportToPDF(xModel, range1);
245 bool bFound = false;
246 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
247 CPPUNIT_ASSERT_EQUAL(true, bFound);
250 // H1:I1
252 ScRange range1(7, 0, 0, 8, 0, 0);
253 exportToPDF(xModel, range1);
254 bool bFound = false;
255 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
256 CPPUNIT_ASSERT_EQUAL(true, bFound);
260 void ScPDFExportTest::testExportFitToPage_Tdf103516()
262 // create test document
263 mxComponent = loadFromDesktop("private:factory/scalc");
264 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
265 uno::Reference<sheet::XSpreadsheetDocument> xDoc(xModel, uno::UNO_QUERY_THROW);
266 uno::Reference<sheet::XSpreadsheets> xSheets(xDoc->getSheets(), UNO_SET_THROW);
267 uno::Reference<container::XIndexAccess> xIndex(xSheets, uno::UNO_QUERY_THROW);
268 xSheets->insertNewByName("First Sheet", 0);
269 uno::Reference<sheet::XSpreadsheet> rSheet(xIndex->getByIndex(0), UNO_QUERY_THROW);
271 // 2. Setup data
273 SfxObjectShell* pFoundShell = SfxObjectShell::GetShellFromComponent(mxComponent);
274 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell);
275 ScDocShellRef xDocSh = dynamic_cast<ScDocShell*>(pFoundShell);
276 CPPUNIT_ASSERT(xDocSh);
278 // put some content into the table
279 ScDocument& rDoc = xDocSh->GetDocument();
280 for (unsigned int r = 0; r < 80; ++r)
281 for (unsigned int c = 0; c < 12; ++c)
282 rDoc.SetValue(ScAddress(c, r, 0), (r + 1) * (c + 1));
285 // A1:G50: 2-page export
287 ScRange range1(0, 0, 0, 6, 49, 0);
288 exportToPDF(xModel, range1);
289 bool bFound = false;
290 CPPUNIT_ASSERT(hasTextInPdf("/Count 2>>", bFound));
291 CPPUNIT_ASSERT_EQUAL(true, bFound);
294 // A1:L80: 4-page export
296 ScRange range1(0, 0, 0, 11, 79, 0);
297 exportToPDF(xModel, range1);
298 bool bFound = false;
299 CPPUNIT_ASSERT(hasTextInPdf("/Count 4>>", bFound));
300 CPPUNIT_ASSERT_EQUAL(true, bFound);
303 // set fit to page: width=1 page, height=0 (automatic)
304 uno::Reference<style::XStyleFamiliesSupplier> xStyleFamSupp(xDoc, UNO_QUERY_THROW);
305 uno::Reference<container::XNameAccess> xStyleFamiliesNames(xStyleFamSupp->getStyleFamilies(),
306 UNO_SET_THROW);
307 uno::Reference<container::XNameAccess> xPageStyles(xStyleFamiliesNames->getByName("PageStyles"),
308 UNO_QUERY_THROW);
309 uno::Any aDefaultStyle = xPageStyles->getByName("Default");
310 uno::Reference<beans::XPropertySet> xProp(aDefaultStyle, UNO_QUERY_THROW);
312 uno::Any aScaleX, aScaleY;
313 sal_Int16 nScale;
314 aScaleX <<= static_cast<sal_Int16>(1);
315 xProp->setPropertyValue("ScaleToPagesX", aScaleX);
316 aScaleX = xProp->getPropertyValue("ScaleToPagesX");
317 aScaleX >>= nScale;
318 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), nScale);
320 aScaleY = xProp->getPropertyValue("ScaleToPagesY");
321 aScaleY >>= nScale;
322 CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nScale);
324 // A1:G50 with fit to page width=1: slightly smaller zoom results only 1-page export
326 ScRange range1(0, 0, 0, 6, 49, 0);
327 exportToPDF(xModel, range1);
328 bool bFound = false;
329 CPPUNIT_ASSERT(hasTextInPdf("/Count 1>>", bFound));
330 CPPUNIT_ASSERT_EQUAL(true, bFound);
333 // A1:L80 with fit to page width=1: slightly smaller zoom results only 1-page export
335 ScRange range1(0, 0, 0, 11, 79, 0);
336 exportToPDF(xModel, range1);
337 bool bFound = false;
338 CPPUNIT_ASSERT(hasTextInPdf("/Count 1>>", bFound));
339 CPPUNIT_ASSERT_EQUAL(true, bFound);
343 void ScPDFExportTest::testUnoCommands_Tdf120161()
345 loadFromURL(u"tdf120161.ods");
347 // A1:G1
349 exportToPDFWithUnoCommands("A1:G1");
350 bool bFound = false;
351 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
352 CPPUNIT_ASSERT_EQUAL(false, bFound);
355 // G1:H1
357 exportToPDFWithUnoCommands("G1:H1");
358 bool bFound = false;
359 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
360 CPPUNIT_ASSERT_EQUAL(true, bFound);
363 // H1:I1
365 exportToPDFWithUnoCommands("H1:I1");
366 bool bFound = false;
367 CPPUNIT_ASSERT(hasTextInPdf("DejaVuSans", bFound));
368 CPPUNIT_ASSERT_EQUAL(true, bFound);
372 void ScPDFExportTest::testTdf64703_hiddenPageBreak()
374 loadFromURL(u"tdf64703_hiddenPageBreak.ods");
376 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
378 // A1:A11: 4-page export
380 ScRange range1(0, 0, 0, 0, 10, 0);
381 exportToPDF(xModel, range1);
382 bool bFound = false;
383 CPPUNIT_ASSERT(hasTextInPdf("/Count 4>>", bFound));
384 CPPUNIT_ASSERT_EQUAL(true, bFound);
388 void ScPDFExportTest::testTdf143978()
390 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
391 if (!pPDFium)
393 return;
396 loadFromURL(u"tdf143978.ods");
397 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
399 // A1:A2
400 ScRange range1(0, 0, 0, 0, 1, 0);
401 exportToPDF(xModel, range1);
402 // Parse the export result with pdfium.
403 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
404 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
406 // Get the first page
407 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
408 CPPUNIT_ASSERT(pPdfPage);
409 std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
411 int nPageObjectCount = pPdfPage->getObjectCount();
412 CPPUNIT_ASSERT_EQUAL(2, nPageObjectCount);
414 // Without the fix in place, this test would have failed with
415 // - Expected: Dies ist viel zu viel Text
416 // - Actual : Dies ist vie
417 std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject1 = pPdfPage->getObject(0);
418 OUString sText1 = pPageObject1->getText(pTextPage);
419 CPPUNIT_ASSERT_EQUAL(OUString("Dies ist viel zu viel Text"), sText1);
421 // and it would also have failed with
422 // - Expected: 2021-11-17
423 // - Actual : ###
424 std::unique_ptr<vcl::pdf::PDFiumPageObject> pPageObject2 = pPdfPage->getObject(1);
425 OUString sText2 = pPageObject2->getText(pTextPage);
426 CPPUNIT_ASSERT_EQUAL(OUString("2021-11-17"), sText2);
429 void ScPDFExportTest::testTdf84012()
431 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
432 if (!pPDFium)
434 return;
437 loadFromURL(u"tdf84012.ods");
438 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
440 // A1
441 ScRange range1(0, 0, 0, 0, 0, 0);
442 exportToPDF(xModel, range1);
443 // Parse the export result with pdfium.
444 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
445 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
447 // Get the first page
448 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
449 CPPUNIT_ASSERT(pPdfPage);
450 std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
452 int nChars = pTextPage->countChars();
453 std::vector<sal_uInt32> aChars(nChars);
454 for (int i = 0; i < nChars; i++)
455 aChars[i] = pTextPage->getUnicode(i);
456 OUString aActualText(aChars.data(), aChars.size());
458 // Without the fix in place, this test would have failed with
459 // - Expected: Blah blah (blah, blah)
460 // - Actual : Blah blah
461 CPPUNIT_ASSERT_EQUAL(OUString("Blah blah (blah, blah)"), aActualText);
464 void ScPDFExportTest::testTdf78897()
466 std::shared_ptr<vcl::pdf::PDFium> pPDFium = vcl::pdf::PDFiumLibrary::get();
467 if (!pPDFium)
469 return;
472 loadFromURL(u"tdf78897.xls");
473 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
475 // C3:D3
476 ScRange range1(2, 2, 0, 3, 2, 0);
477 exportToPDF(xModel, range1);
478 // Parse the export result with pdfium.
479 std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
480 CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
482 // Get the first page
483 std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex=*/0);
484 CPPUNIT_ASSERT(pPdfPage);
485 std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
487 int nChars = pTextPage->countChars();
488 std::vector<sal_uInt32> aChars(nChars);
489 for (int i = 0; i < nChars; i++)
490 aChars[i] = pTextPage->getUnicode(i);
491 OUString aActualText(aChars.data(), aChars.size());
493 // Without the fix in place, this test would have failed with
494 // - Expected: 11.00 11.00
495 // - Actual : 11.00 ###
496 CPPUNIT_ASSERT_EQUAL(OUString(" 11.00 11.00 "), aActualText);
499 // just needs to not crash on export to pdf
500 void ScPDFExportTest::testForcepoint97()
502 loadFromURL(u"forcepoint97.xlsx");
503 uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
505 // A1:H81
506 ScRange range1(0, 0, 0, 7, 81, 0);
507 exportToPDF(xModel, range1);
510 CPPUNIT_TEST_SUITE_REGISTRATION(ScPDFExportTest);
511 CPPUNIT_PLUGIN_IMPLEMENT();
513 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */