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>
12 #include <com/sun/star/frame/Desktop.hpp>
13 #include <com/sun/star/frame/XStorable.hpp>
14 #include <com/sun/star/lang/XComponent.hpp>
15 #include <com/sun/star/sheet/XSpreadsheet.hpp>
16 #include <com/sun/star/table/XCellRange.hpp>
17 #include <com/sun/star/view/XSelectionSupplier.hpp>
18 #include <test/bootstrapfixture.hxx>
19 #include <unotools/tempfile.hxx>
20 #include <unotest/macros_test.hxx>
22 #include <editutil.hxx>
23 #include <editeng/eeitem.hxx>
24 #include <editeng/fontitem.hxx>
25 #include <osl/file.hxx>
26 #include <comphelper/processfactory.hxx>
28 using namespace css::lang
;
29 using namespace ::com::sun::star
;
30 using namespace ::com::sun::star::uno
;
32 class ScPDFExportTest
: public test::BootstrapFixture
, public unotest::MacrosTest
34 Reference
<XComponent
> mxComponent
;
35 Reference
<frame::XFrame
> xTargetFrame
;
40 virtual void setUp() override final
;
41 virtual void tearDown() override final
;
45 std::shared_ptr
<utl::TempFile
> exportToPDF(const uno::Reference
<frame::XModel
>& xModel
,
46 const ScRange
& range
);
48 static bool hasTextInPdf(const std::shared_ptr
<utl::TempFile
>& pPDFFile
, const char* sText
,
51 void setFont(ScFieldEditEngine
& rEE
, sal_Int32 nStart
, sal_Int32 nEnd
,
52 const OUString
& rFontName
);
56 void testExportRange_Tdf120161();
57 void testExportFitToPage_Tdf103516();
59 CPPUNIT_TEST_SUITE(ScPDFExportTest
);
60 CPPUNIT_TEST(testExportRange_Tdf120161
);
61 CPPUNIT_TEST(testExportFitToPage_Tdf103516
);
62 CPPUNIT_TEST_SUITE_END();
65 void ScPDFExportTest::setUp()
67 test::BootstrapFixture::setUp();
70 css::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())));
73 uno::Reference
<frame::XDesktop2
> xDesktop
= mxDesktop
;
74 CPPUNIT_ASSERT(xDesktop
.is());
77 uno::Sequence
<beans::PropertyValue
> args(1);
78 args
[0].Name
= "Hidden";
79 args
[0].Value
<<= true;
80 mxComponent
= xDesktop
->loadComponentFromURL("private:factory/scalc", "_blank", 0, args
);
81 CPPUNIT_ASSERT(mxComponent
.is());
84 xTargetFrame
= xDesktop
->findFrame("_blank", 0);
85 CPPUNIT_ASSERT(xTargetFrame
.is());
87 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
88 uno::Reference
<frame::XModel2
> xModel2(xModel
, UNO_QUERY
);
89 CPPUNIT_ASSERT(xModel2
.is());
91 Reference
<frame::XController2
> xController
92 = xModel2
->createDefaultViewController(xTargetFrame
);
93 CPPUNIT_ASSERT(xController
.is());
95 // introduce model/view/controller to each other
96 xController
->attachModel(xModel2
.get());
97 xModel2
->connectController(xController
.get());
99 xTargetFrame
->setComponent(xController
->getComponentWindow(), xController
.get());
100 xController
->attachFrame(xTargetFrame
);
102 xModel2
->setCurrentController(xController
.get());
106 void ScPDFExportTest::tearDown()
108 if (mxComponent
.is())
109 mxComponent
->dispose();
111 test::BootstrapFixture::tearDown();
114 bool ScPDFExportTest::hasTextInPdf(const std::shared_ptr
<utl::TempFile
>& pPDFFile
,
115 const char* sText
, bool& bFound
)
117 SvStream
* pStream
= pPDFFile
->GetStream(StreamMode::STD_READ
);
118 CPPUNIT_ASSERT(pStream
);
121 pStream
->Seek(STREAM_SEEK_TO_END
);
122 const std::size_t nFileSize
= pStream
->Tell();
127 char* pBuffer
= new char[nFileSize
];
128 pStream
->Seek(STREAM_SEEK_TO_BEGIN
);
129 const std::size_t nRead
= pStream
->ReadBytes(pBuffer
, nFileSize
);
130 if (nRead
== nFileSize
)
132 const std::string
haystack(pBuffer
, pBuffer
+ nFileSize
);
133 const std::string
needle(sText
);
134 const std::size_t n
= haystack
.find(needle
);
135 bFound
= (n
!= std::string::npos
);
139 // close and return the status
141 pPDFFile
->CloseStream();
142 return (nRead
== nFileSize
);
145 std::shared_ptr
<utl::TempFile
>
146 ScPDFExportTest::exportToPDF(const uno::Reference
<frame::XModel
>& xModel
, const ScRange
& range
)
148 // create temp file name
149 auto pTempFile
= std::make_shared
<utl::TempFile
>();
150 pTempFile
->EnableKillingFile();
151 OUString sFileURL
= pTempFile
->GetURL();
152 // Note: under Windows path path should be with "/" delimiters instead of "\\"
153 // due to usage of INetURLObject() that converts "\\" to hexadecimal notation.
154 ::osl::FileBase::getFileURLFromSystemPath(sFileURL
, sFileURL
);
157 uno::Reference
<sheet::XSpreadsheetDocument
> xDoc(xModel
, uno::UNO_QUERY_THROW
);
158 uno::Reference
<sheet::XSpreadsheets
> xSheets(xDoc
->getSheets(), UNO_SET_THROW
);
159 uno::Reference
<container::XIndexAccess
> xIndex(xSheets
, uno::UNO_QUERY_THROW
);
160 uno::Reference
<sheet::XSpreadsheet
> rSheet(xIndex
->getByIndex(0), UNO_QUERY_THROW
);
162 // select requested cells to print
163 // query for the XCellRange interface
164 uno::Reference
<table::XCellRange
> xCellRange
= rSheet
->getCellRangeByPosition(
165 range
.aStart
.Col(), range
.aStart
.Row(), range
.aEnd
.Col(), range
.aEnd
.Row());
167 uno::Reference
<frame::XController
> xController
= xModel
->getCurrentController();
168 CPPUNIT_ASSERT(xController
.is());
170 uno::Reference
<view::XSelectionSupplier
> xSelection(xController
, uno::UNO_QUERY_THROW
);
171 CPPUNIT_ASSERT(xSelection
.is());
173 uno::Any
rCellRangeAny(xCellRange
);
174 xSelection
->select(rCellRangeAny
);
177 // init special pdf export params
178 css::uno::Sequence
<css::beans::PropertyValue
> aFilterData(3);
179 aFilterData
[0].Name
= "Selection";
180 aFilterData
[0].Value
<<= xCellRange
;
181 aFilterData
[1].Name
= "Printing";
182 aFilterData
[1].Value
<<= sal_Int32(2);
183 aFilterData
[2].Name
= "ViewPDFAfterExport";
184 aFilterData
[2].Value
<<= true;
186 // init set of params for storeToURL() call
187 css::uno::Sequence
<css::beans::PropertyValue
> seqArguments(3);
188 seqArguments
[0].Name
= "FilterData";
189 seqArguments
[0].Value
<<= aFilterData
;
190 seqArguments
[1].Name
= "FilterName";
191 seqArguments
[1].Value
<<= OUString("calc_pdf_Export");
192 seqArguments
[2].Name
= "URL";
193 seqArguments
[2].Value
<<= sFileURL
;
196 uno::Reference
<lang::XComponent
> xComponent(mxComponent
, UNO_SET_THROW
);
197 uno::Reference
<css::frame::XStorable
> xStorable(xComponent
, UNO_QUERY
);
198 xStorable
->storeToURL(sFileURL
, seqArguments
);
200 // return file object with generated PDF
204 void ScPDFExportTest::setFont(ScFieldEditEngine
& rEE
, sal_Int32 nStart
, sal_Int32 nEnd
,
205 const OUString
& rFontName
)
208 aSel
.nStartPara
= aSel
.nEndPara
= 0;
209 aSel
.nStartPos
= nStart
;
212 SfxItemSet aItemSet
= rEE
.GetEmptyItemSet();
213 SvxFontItem
aItem(FAMILY_MODERN
, rFontName
, "", PITCH_VARIABLE
, RTL_TEXTENCODING_UTF8
,
216 rEE
.QuickSetAttribs(aItemSet
, aSel
);
219 // Selection was not taken into account during export into PDF
220 void ScPDFExportTest::testExportRange_Tdf120161()
222 // create test document
223 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
224 uno::Reference
<sheet::XSpreadsheetDocument
> xDoc(xModel
, uno::UNO_QUERY_THROW
);
225 uno::Reference
<sheet::XSpreadsheets
> xSheets(xDoc
->getSheets(), UNO_SET_THROW
);
226 uno::Reference
<container::XIndexAccess
> xIndex(xSheets
, uno::UNO_QUERY_THROW
);
227 xSheets
->insertNewByName("First Sheet", 0);
228 uno::Reference
<sheet::XSpreadsheet
> rSheet(xIndex
->getByIndex(0), UNO_QUERY_THROW
);
232 SfxObjectShell
* pFoundShell
= SfxObjectShell::GetShellFromComponent(mxComponent
);
233 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell
);
234 ScDocShellRef xDocSh
= dynamic_cast<ScDocShell
*>(pFoundShell
);
235 CPPUNIT_ASSERT(xDocSh
);
237 // put some content into the first row with default font
238 ScDocument
& rDoc
= xDocSh
->GetDocument();
239 for (unsigned int r
= 0; r
< 1; ++r
)
240 for (unsigned int c
= 0; c
< 14; ++c
)
241 rDoc
.SetValue(ScAddress(c
, r
, 0), (r
+ 1) * (c
+ 1));
243 // set "Text" to H1 cell with "DejaVuSans" font
244 ScFieldEditEngine
& rEE
= rDoc
.GetEditEngine();
246 rEE
.SetTextCurrentDefaults("Text");
247 setFont(rEE
, 0, 4, "DejaVuSans"); // set font for first 4 chars
248 rDoc
.SetEditText(ScAddress(7, 0, 0), rEE
.CreateTextObject());
253 ScRange
range1(0, 0, 0, 6, 0, 0);
254 std::shared_ptr
<utl::TempFile
> pPDFFile
= exportToPDF(xModel
, range1
);
256 CPPUNIT_ASSERT(hasTextInPdf(pPDFFile
, "DejaVuSans", bFound
));
257 CPPUNIT_ASSERT_EQUAL(false, bFound
);
262 ScRange
range1(6, 0, 0, 7, 0, 0);
263 std::shared_ptr
<utl::TempFile
> pPDFFile
= exportToPDF(xModel
, range1
);
265 CPPUNIT_ASSERT(hasTextInPdf(pPDFFile
, "DejaVuSans", bFound
));
266 CPPUNIT_ASSERT_EQUAL(true, bFound
);
271 ScRange
range1(7, 0, 0, 8, 0, 0);
272 std::shared_ptr
<utl::TempFile
> pPDFFile
= exportToPDF(xModel
, range1
);
274 CPPUNIT_ASSERT(hasTextInPdf(pPDFFile
, "DejaVuSans", bFound
));
275 CPPUNIT_ASSERT_EQUAL(true, bFound
);
279 void ScPDFExportTest::testExportFitToPage_Tdf103516()
281 // create test document
282 uno::Reference
<frame::XModel
> xModel(mxComponent
, uno::UNO_QUERY
);
283 uno::Reference
<sheet::XSpreadsheetDocument
> xDoc(xModel
, uno::UNO_QUERY_THROW
);
284 uno::Reference
<sheet::XSpreadsheets
> xSheets(xDoc
->getSheets(), UNO_SET_THROW
);
285 uno::Reference
<container::XIndexAccess
> xIndex(xSheets
, uno::UNO_QUERY_THROW
);
286 xSheets
->insertNewByName("First Sheet", 0);
287 uno::Reference
<sheet::XSpreadsheet
> rSheet(xIndex
->getByIndex(0), UNO_QUERY_THROW
);
291 SfxObjectShell
* pFoundShell
= SfxObjectShell::GetShellFromComponent(mxComponent
);
292 CPPUNIT_ASSERT_MESSAGE("Failed to access document shell", pFoundShell
);
293 ScDocShellRef xDocSh
= dynamic_cast<ScDocShell
*>(pFoundShell
);
294 CPPUNIT_ASSERT(xDocSh
);
296 // put some content into the table
297 ScDocument
& rDoc
= xDocSh
->GetDocument();
298 for (unsigned int r
= 0; r
< 80; ++r
)
299 for (unsigned int c
= 0; c
< 12; ++c
)
300 rDoc
.SetValue(ScAddress(c
, r
, 0), (r
+ 1) * (c
+ 1));
303 // A1:G50: 2-page export
305 ScRange
range1(0, 0, 0, 6, 49, 0);
306 std::shared_ptr
<utl::TempFile
> pPDFFile
= exportToPDF(xModel
, range1
);
308 CPPUNIT_ASSERT(hasTextInPdf(pPDFFile
, "/Count 2>>", bFound
));
309 CPPUNIT_ASSERT_EQUAL(true, bFound
);
312 // A1:L80: 4-page export
314 ScRange
range1(0, 0, 0, 11, 79, 0);
315 std::shared_ptr
<utl::TempFile
> pPDFFile
= exportToPDF(xModel
, range1
);
317 CPPUNIT_ASSERT(hasTextInPdf(pPDFFile
, "/Count 4>>", bFound
));
318 CPPUNIT_ASSERT_EQUAL(true, bFound
);
321 // set fit to page: width=1 page, height=0 (automatic)
322 uno::Reference
<style::XStyleFamiliesSupplier
> xStyleFamSupp(xDoc
, UNO_QUERY_THROW
);
323 uno::Reference
<container::XNameAccess
> xStyleFamiliesNames(xStyleFamSupp
->getStyleFamilies(),
325 uno::Reference
<container::XNameAccess
> xPageStyles(xStyleFamiliesNames
->getByName("PageStyles"),
327 uno::Any aDefaultStyle
= xPageStyles
->getByName("Default");
328 uno::Reference
<beans::XPropertySet
> xProp(aDefaultStyle
, UNO_QUERY_THROW
);
330 uno::Any aScaleX
, aScaleY
;
332 aScaleX
<<= static_cast<sal_Int16
>(1);
333 xProp
->setPropertyValue("ScaleToPagesX", aScaleX
);
334 aScaleX
= xProp
->getPropertyValue("ScaleToPagesX");
336 CPPUNIT_ASSERT_EQUAL(sal_Int16(1), nScale
);
338 aScaleY
= xProp
->getPropertyValue("ScaleToPagesY");
340 CPPUNIT_ASSERT_EQUAL(sal_Int16(0), nScale
);
342 // A1:G50 with fit to page width=1: slightly smaller zoom results only 1-page export
344 ScRange
range1(0, 0, 0, 6, 49, 0);
345 std::shared_ptr
<utl::TempFile
> pPDFFile
= exportToPDF(xModel
, range1
);
347 CPPUNIT_ASSERT(hasTextInPdf(pPDFFile
, "/Count 1>>", bFound
));
348 CPPUNIT_ASSERT_EQUAL(true, bFound
);
351 // A1:L80 with fit to page width=1: slightly smaller zoom results only 1-page export
353 ScRange
range1(0, 0, 0, 11, 79, 0);
354 std::shared_ptr
<utl::TempFile
> pPDFFile
= exportToPDF(xModel
, range1
);
356 CPPUNIT_ASSERT(hasTextInPdf(pPDFFile
, "/Count 1>>", bFound
));
357 CPPUNIT_ASSERT_EQUAL(true, bFound
);
361 CPPUNIT_TEST_SUITE_REGISTRATION(ScPDFExportTest
);
362 CPPUNIT_PLUGIN_IMPLEMENT();
364 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */