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 "charttest.hxx"
12 #include <com/sun/star/chart/ErrorBarStyle.hpp>
13 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
14 #include <com/sun/star/chart2/MovingAverageType.hpp>
15 #include <com/sun/star/lang/XServiceName.hpp>
16 #include <com/sun/star/text/XTextDocument.hpp>
17 #include <com/sun/star/drawing/LineStyle.hpp>
18 #include <com/sun/star/drawing/FillStyle.hpp>
19 #include <com/sun/star/chart2/DataPointLabel.hpp>
20 #include <com/sun/star/chart/DataLabelPlacement.hpp>
23 using beans::XPropertySet
;
25 class Chart2ExportTest
: public ChartTest
28 Chart2ExportTest() : ChartTest("/chart2/qa/extras/data/") {}
33 void testErrorBar( Reference
< XPropertySet
> const & xErrorBar
)
35 sal_Int32 nErrorBarStyle
;
37 xErrorBar
->getPropertyValue("ErrorBarStyle") >>= nErrorBarStyle
);
38 CPPUNIT_ASSERT_EQUAL(chart::ErrorBarStyle::RELATIVE
, nErrorBarStyle
);
39 bool bShowPositive
= bool(), bShowNegative
= bool();
41 xErrorBar
->getPropertyValue("ShowPositiveError") >>= bShowPositive
);
42 CPPUNIT_ASSERT(bShowPositive
);
44 xErrorBar
->getPropertyValue("ShowNegativeError") >>= bShowNegative
);
45 CPPUNIT_ASSERT(bShowNegative
);
47 CPPUNIT_ASSERT(xErrorBar
->getPropertyValue("PositiveError") >>= nVal
);
48 CPPUNIT_ASSERT_DOUBLES_EQUAL(10.0, nVal
, 1e-10);
51 void checkCommonTrendline(
52 Reference
<chart2::XRegressionCurve
> const & xCurve
,
53 double aExpectedExtrapolateForward
, double aExpectedExtrapolateBackward
,
54 bool aExpectedForceIntercept
, double aExpectedInterceptValue
,
55 bool aExpectedShowEquation
, bool aExpectedR2
, bool aExpectedMayHaveR2
)
57 Reference
<XPropertySet
> xProperties( xCurve
, uno::UNO_QUERY
);
58 CPPUNIT_ASSERT(xProperties
.is());
60 double aExtrapolateForward
= 0.0;
61 CPPUNIT_ASSERT(xProperties
->getPropertyValue("ExtrapolateForward") >>= aExtrapolateForward
);
62 CPPUNIT_ASSERT_EQUAL(aExpectedExtrapolateForward
, aExtrapolateForward
);
64 double aExtrapolateBackward
= 0.0;
65 CPPUNIT_ASSERT(xProperties
->getPropertyValue("ExtrapolateBackward") >>= aExtrapolateBackward
);
66 CPPUNIT_ASSERT_EQUAL(aExpectedExtrapolateBackward
, aExtrapolateBackward
);
68 bool bForceIntercept
= false;
69 CPPUNIT_ASSERT(xProperties
->getPropertyValue("ForceIntercept") >>= bForceIntercept
);
70 CPPUNIT_ASSERT_EQUAL(aExpectedForceIntercept
, bForceIntercept
);
74 double aInterceptValue
= 0.0;
75 CPPUNIT_ASSERT(xProperties
->getPropertyValue("InterceptValue") >>= aInterceptValue
);
76 CPPUNIT_ASSERT_EQUAL(aExpectedInterceptValue
, aInterceptValue
);
79 Reference
< XPropertySet
> xEquationProperties( xCurve
->getEquationProperties() );
80 CPPUNIT_ASSERT(xEquationProperties
.is());
82 bool bShowEquation
= false;
83 CPPUNIT_ASSERT(xEquationProperties
->getPropertyValue("ShowEquation") >>= bShowEquation
);
84 CPPUNIT_ASSERT_EQUAL(aExpectedShowEquation
, bShowEquation
);
86 bool bShowCorrelationCoefficient
= false;
87 CPPUNIT_ASSERT(xEquationProperties
->getPropertyValue("ShowCorrelationCoefficient") >>= bShowCorrelationCoefficient
);
88 CPPUNIT_ASSERT_EQUAL(aExpectedR2
, bShowCorrelationCoefficient
);
90 bool bMayHaveR2
= false;
91 CPPUNIT_ASSERT(xEquationProperties
->getPropertyValue("MayHaveCorrelationCoefficient") >>= bMayHaveR2
);
92 CPPUNIT_ASSERT_EQUAL(aExpectedMayHaveR2
, bMayHaveR2
);
95 void checkNameAndType(Reference
<XPropertySet
> const & xProperties
, const OUString
& aExpectedName
, const OUString
& aExpectedServiceName
)
97 Reference
< lang::XServiceName
> xServiceName( xProperties
, UNO_QUERY
);
98 CPPUNIT_ASSERT(xServiceName
.is());
100 OUString aServiceName
= xServiceName
->getServiceName();
101 CPPUNIT_ASSERT_EQUAL(aExpectedServiceName
, aServiceName
);
104 CPPUNIT_ASSERT(xProperties
->getPropertyValue("CurveName") >>= aCurveName
);
105 CPPUNIT_ASSERT_EQUAL(aExpectedName
, aCurveName
);
108 void checkLinearTrendline(
109 Reference
<chart2::XRegressionCurve
> const & xCurve
, const OUString
& aExpectedName
,
110 double aExpectedExtrapolateForward
, double aExpectedExtrapolateBackward
,
111 double aExpectedInterceptValue
)
113 Reference
<XPropertySet
> xProperties( xCurve
, uno::UNO_QUERY
);
114 CPPUNIT_ASSERT(xProperties
.is());
116 checkNameAndType(xProperties
, aExpectedName
, "com.sun.star.chart2.LinearRegressionCurve");
118 checkCommonTrendline(
120 aExpectedExtrapolateForward
, aExpectedExtrapolateBackward
,
121 /*aExpectedForceIntercept*/false, aExpectedInterceptValue
,
122 /*aExpectedShowEquation*/true, /*aExpectedR2*/false, /*aExpectedMayHaveR2*/true);
125 void checkPolynomialTrendline(
126 Reference
<chart2::XRegressionCurve
> const & xCurve
, const OUString
& aExpectedName
,
127 sal_Int32 aExpectedDegree
,
128 double aExpectedExtrapolateForward
, double aExpectedExtrapolateBackward
,
129 double aExpectedInterceptValue
)
131 Reference
<XPropertySet
> xProperties( xCurve
, uno::UNO_QUERY
);
132 CPPUNIT_ASSERT(xProperties
.is());
134 checkNameAndType(xProperties
, aExpectedName
, "com.sun.star.chart2.PolynomialRegressionCurve");
136 sal_Int32 aDegree
= 2;
137 CPPUNIT_ASSERT(xProperties
->getPropertyValue("PolynomialDegree") >>= aDegree
);
138 CPPUNIT_ASSERT_EQUAL(aExpectedDegree
, aDegree
);
140 checkCommonTrendline(
142 aExpectedExtrapolateForward
, aExpectedExtrapolateBackward
,
143 /*aExpectedForceIntercept*/true, aExpectedInterceptValue
,
144 /*aExpectedShowEquation*/true, /*aExpectedR2*/true, /*aExpectedMayHaveR2*/true);
147 void checkMovingAverageTrendline(
148 Reference
<chart2::XRegressionCurve
> const & xCurve
, const OUString
& aExpectedName
, sal_Int32 aExpectedPeriod
)
150 Reference
<XPropertySet
> xProperties( xCurve
, uno::UNO_QUERY
);
151 CPPUNIT_ASSERT(xProperties
.is());
153 checkNameAndType(xProperties
, aExpectedName
, "com.sun.star.chart2.MovingAverageRegressionCurve");
155 sal_Int32 aPeriod
= 2;
156 CPPUNIT_ASSERT(xProperties
->getPropertyValue("MovingAveragePeriod") >>= aPeriod
);
157 CPPUNIT_ASSERT_EQUAL(aExpectedPeriod
, aPeriod
);
159 checkCommonTrendline(
161 /*aExpectedExtrapolateForward*/0.0, /*aExpectedExtrapolateBackward*/0.0,
162 /*aExpectedForceIntercept*/false, /*aExpectedInterceptValue*/0.0,
163 /*aExpectedShowEquation*/false, /*aExpectedR2*/false, /*aExpectedMayHaveR2*/false);
166 void checkTrendlinesInChart(uno::Reference
< chart2::XChartDocument
> const & xChartDoc
)
168 CPPUNIT_ASSERT(xChartDoc
.is());
170 Reference
< chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc( xChartDoc
, 0 );
171 CPPUNIT_ASSERT( xDataSeries
.is() );
173 Reference
< chart2::XRegressionCurveContainer
> xRegressionCurveContainer( xDataSeries
, UNO_QUERY
);
174 CPPUNIT_ASSERT( xRegressionCurveContainer
.is() );
176 Sequence
< Reference
< chart2::XRegressionCurve
> > xRegressionCurveSequence
= xRegressionCurveContainer
->getRegressionCurves();
177 CPPUNIT_ASSERT_EQUAL(sal_Int32(3), xRegressionCurveSequence
.getLength());
179 Reference
<chart2::XRegressionCurve
> xCurve
;
181 xCurve
= xRegressionCurveSequence
[0];
182 CPPUNIT_ASSERT(xCurve
.is());
183 checkPolynomialTrendline(xCurve
, "col2_poly", 3, 0.1, -0.1, -1.0);
185 xCurve
= xRegressionCurveSequence
[1];
186 CPPUNIT_ASSERT(xCurve
.is());
187 checkLinearTrendline(xCurve
, "col2_linear", -0.5, -0.5, 0.0);
189 xCurve
= xRegressionCurveSequence
[2];
190 CPPUNIT_ASSERT(xCurve
.is());
191 checkMovingAverageTrendline(xCurve
, "col2_moving_avg", 3);
197 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testErrorBarXLSX
)
199 loadFromFile(u
"ods/error_bar.ods");
201 // make sure the ODS import was successful
202 uno::Reference
< chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
203 CPPUNIT_ASSERT(xChartDoc
.is());
205 Reference
< chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc( xChartDoc
, 0 );
206 CPPUNIT_ASSERT( xDataSeries
.is() );
208 Reference
< beans::XPropertySet
> xPropSet( xDataSeries
, UNO_QUERY_THROW
);
210 // test that y error bars are there
211 Reference
< beans::XPropertySet
> xErrorBarYProps
;
212 xPropSet
->getPropertyValue(CHART_UNONAME_ERRORBAR_Y
) >>= xErrorBarYProps
;
213 testErrorBar(xErrorBarYProps
);
216 saveAndReload("Calc Office Open XML");
218 uno::Reference
< chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
219 CPPUNIT_ASSERT(xChartDoc
.is());
221 Reference
< chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc( xChartDoc
, 0 );
222 CPPUNIT_ASSERT( xDataSeries
.is() );
224 Reference
< beans::XPropertySet
> xPropSet( xDataSeries
, UNO_QUERY_THROW
);
226 // test that y error bars are there
227 Reference
< beans::XPropertySet
> xErrorBarYProps
;
228 xPropSet
->getPropertyValue(CHART_UNONAME_ERRORBAR_Y
) >>= xErrorBarYProps
;
229 testErrorBar(xErrorBarYProps
);
233 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testErrorBarPropXLSX
)
235 loadFromFile(u
"xlsx/testErrorBarProp.xlsx");
236 save("Calc Office Open XML");
237 xmlDocUniquePtr pXmlDoc
= parseExport("xl/charts/chart1.xml");
238 CPPUNIT_ASSERT(pXmlDoc
);
240 // test y error bars property
241 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:errBars[1]/c:errDir"_ostr
, "val"_ostr
, "y");
242 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:errBars[1]/c:spPr/a:ln"_ostr
, "w"_ostr
, "12600");
243 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:errBars[1]/c:spPr/a:ln/a:solidFill/a:srgbClr"_ostr
, "val"_ostr
, "ff0000");
245 // test x error bars property
246 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:errBars[2]/c:errDir"_ostr
, "val"_ostr
, "x");
247 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:errBars[2]/c:spPr/a:ln"_ostr
, "w"_ostr
, "9360");
248 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser/c:errBars[2]/c:spPr/a:ln/a:solidFill/a:srgbClr"_ostr
, "val"_ostr
, "595959");
251 // This method tests the preservation of properties for trendlines / regression curves
252 // in an export -> import cycle using different file formats - ODS, XLS and XLSX.
253 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testTrendline
)
255 // Validation fails with
256 // Error: tag name "chart:symbol-image" is not allowed. Possible tag names are: <label-separator>
258 loadFromFile(u
"ods/trendline.ods");
259 checkTrendlinesInChart(getChartDocFromSheet( 0, mxComponent
));
260 saveAndReload("calc8");
261 checkTrendlinesInChart(getChartDocFromSheet( 0, mxComponent
));
264 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testTrendlineOOXML
)
266 loadFromFile(u
"ods/trendline.ods");
267 checkTrendlinesInChart(getChartDocFromSheet( 0, mxComponent
));
268 saveAndReload("Calc Office Open XML");
269 checkTrendlinesInChart(getChartDocFromSheet( 0, mxComponent
));
272 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testTrendlineXLS
)
274 loadFromFile(u
"ods/trendline.ods");
275 checkTrendlinesInChart(getChartDocFromSheet( 0, mxComponent
));
276 saveAndReload("MS Excel 97");
277 checkTrendlinesInChart(getChartDocFromSheet( 0, mxComponent
));
280 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testMovingAverage
)
282 loadFromFile(u
"ods/moving-type.ods");
283 saveAndReload("calc8");
285 uno::Reference
< chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
286 CPPUNIT_ASSERT(xChartDoc
.is());
288 Reference
< chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc( xChartDoc
, 0 );
289 CPPUNIT_ASSERT( xDataSeries
.is() );
291 Reference
< chart2::XRegressionCurveContainer
> xRegressionCurveContainer( xDataSeries
, UNO_QUERY
);
292 CPPUNIT_ASSERT( xRegressionCurveContainer
.is() );
294 Sequence
< Reference
< chart2::XRegressionCurve
> > xRegressionCurveSequence
= xRegressionCurveContainer
->getRegressionCurves();
295 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xRegressionCurveSequence
.getLength());
297 Reference
<chart2::XRegressionCurve
> xCurve
= xRegressionCurveSequence
[0];
298 CPPUNIT_ASSERT(xCurve
.is());
300 Reference
<XPropertySet
> xProperties( xCurve
, uno::UNO_QUERY
);
301 CPPUNIT_ASSERT(xProperties
.is());
303 sal_Int32 nMovingAverageType
= 0;
304 xProperties
->getPropertyValue("MovingAverageType") >>= nMovingAverageType
;
305 CPPUNIT_ASSERT_EQUAL(chart2::MovingAverageType::Central
, nMovingAverageType
);
308 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testStockChart
)
310 /* For attached file Stock_Chart.docx, in chart1.xml,
311 * <c:stockChart>, there are four types of series as
312 * Open,Low,High and Close.
313 * For Open series, in <c:idx val="0" />
314 * an attribute val of index should start from 1 and not from 0.
315 * Which was problem area.
317 loadFromFile(u
"docx/testStockChart.docx");
319 save("Office Open XML Text");
320 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
321 CPPUNIT_ASSERT(pXmlDoc
);
323 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:stockChart/c:ser[1]/c:idx"_ostr
, "val"_ostr
, "1");
324 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:stockChart/c:ser[1]/c:order"_ostr
, "val"_ostr
, "1");
327 "/c:chartSpace/c:chart/c:plotArea/c:stockChart/c:ser[1]/c:tx/c:strRef/c:strCache/c:pt/c:v"_ostr
,
331 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testBarChart
)
333 loadFromFile(u
"docx/testBarChart.docx");
334 save("Office Open XML Text");
335 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
336 CPPUNIT_ASSERT(pXmlDoc
);
338 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:barDir"_ostr
, "val"_ostr
, "col");
341 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testCrosses
)
343 // test crosses val="autoZero" with DOCX
345 loadFromFile(u
"docx/Bar_horizontal_cone.docx");
346 save("Office Open XML Text");
347 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
349 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:catAx/c:crosses"_ostr
, "val"_ostr
, "autoZero");
351 // tdf#142351: test crossesAt val="-50" with XLSX
353 loadFromFile(u
"xlsx/tdf142351.xlsx");
354 save("Calc Office Open XML");
355 xmlDocUniquePtr pXmlDoc
= parseExport("xl/charts/chart1.xml");
356 CPPUNIT_ASSERT(pXmlDoc
);
358 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:catAx/c:crossesAt"_ostr
, "val"_ostr
, "-50");
362 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testScatterChartTextXValues
)
364 loadFromFile(u
"docx/scatter-chart-text-x-values.docx");
366 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
367 CPPUNIT_ASSERT(xChartDoc
.is());
369 Reference
<chart2::XChartType
> xCT
= getChartTypeFromDoc(xChartDoc
, 0);
370 CPPUNIT_ASSERT(xCT
.is());
372 // Make sure we have exactly 3 data series.
373 std::vector
<uno::Sequence
<uno::Any
> > aLabels
= getDataSeriesLabelsFromChartType(xCT
);
374 CPPUNIT_ASSERT_EQUAL(size_t(3), aLabels
.size());
375 CPPUNIT_ASSERT_EQUAL(OUString("Series 1"), aLabels
[0][0].get
<OUString
>());
376 CPPUNIT_ASSERT_EQUAL(OUString("Series 2"), aLabels
[1][0].get
<OUString
>());
377 CPPUNIT_ASSERT_EQUAL(OUString("Series 3"), aLabels
[2][0].get
<OUString
>());
379 std::vector
<std::vector
<double> > aYValues
= getDataSeriesYValuesFromChartType(xCT
);
380 CPPUNIT_ASSERT_EQUAL(size_t(3), aYValues
.size());
382 // Check the Y values of "Series 1".
383 CPPUNIT_ASSERT_EQUAL(size_t(4), aYValues
[0].size());
384 CPPUNIT_ASSERT_EQUAL(4.3, aYValues
[0][0]);
385 CPPUNIT_ASSERT_EQUAL(2.5, aYValues
[0][1]);
386 CPPUNIT_ASSERT_EQUAL(3.5, aYValues
[0][2]);
387 CPPUNIT_ASSERT_EQUAL(4.5, aYValues
[0][3]);
390 CPPUNIT_ASSERT_EQUAL(size_t(4), aYValues
[1].size());
391 CPPUNIT_ASSERT_EQUAL(2.4, aYValues
[1][0]);
392 CPPUNIT_ASSERT_EQUAL(4.4, aYValues
[1][1]);
393 CPPUNIT_ASSERT_EQUAL(1.8, aYValues
[1][2]);
394 CPPUNIT_ASSERT_EQUAL(2.8, aYValues
[1][3]);
397 CPPUNIT_ASSERT_EQUAL(size_t(4), aYValues
[2].size());
398 CPPUNIT_ASSERT_EQUAL(2.0, aYValues
[2][0]);
399 CPPUNIT_ASSERT_EQUAL(2.0, aYValues
[2][1]);
400 CPPUNIT_ASSERT_EQUAL(3.0, aYValues
[2][2]);
401 CPPUNIT_ASSERT_EQUAL(5.0, aYValues
[2][3]);
404 save("Office Open XML Text");
405 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
406 CPPUNIT_ASSERT(pXmlDoc
);
408 assertXPathContent(pXmlDoc
, "//c:scatterChart/c:ser[1]/c:xVal[1]/c:numRef[1]/c:numCache[1]/c:pt[1]/c:v[1]"_ostr
, "1");
411 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testScatterXAxisValues
)
413 loadFromFile(u
"odt/tdf114657.odt");
415 save("Office Open XML Text");
416 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
417 CPPUNIT_ASSERT(pXmlDoc
);
419 assertXPath(pXmlDoc
, "//c:scatterChart/c:ser/c:xVal/c:numRef/c:numCache/c:ptCount"_ostr
, "val"_ostr
, "5");
420 assertXPathContent(pXmlDoc
, "//c:scatterChart/c:ser/c:xVal/c:numRef/c:numCache/c:pt[1]/c:v"_ostr
, "15");
421 assertXPathContent(pXmlDoc
, "//c:scatterChart/c:ser/c:xVal/c:numRef/c:numCache/c:pt[2]/c:v"_ostr
, "11");
422 assertXPathContent(pXmlDoc
, "//c:scatterChart/c:ser/c:xVal/c:numRef/c:numCache/c:pt[3]/c:v"_ostr
, "20");
423 assertXPathContent(pXmlDoc
, "//c:scatterChart/c:ser/c:xVal/c:numRef/c:numCache/c:pt[4]/c:v"_ostr
, "16");
426 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testScatterXAxisCategories
)
428 loadFromFile(u
"odt/tdf131143.odt");
430 save("Office Open XML Text");
431 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
432 CPPUNIT_ASSERT(pXmlDoc
);
433 assertXPath(pXmlDoc
, "//c:scatterChart/c:ser[1]/c:xVal/c:strRef/c:strCache/c:ptCount"_ostr
, "val"_ostr
, "4");
434 assertXPathContent(pXmlDoc
, "//c:scatterChart/c:ser[1]/c:xVal/c:strRef/c:strCache/c:pt[1]/c:v"_ostr
, "Row 1");
435 assertXPathContent(pXmlDoc
, "//c:scatterChart/c:ser[1]/c:xVal/c:strRef/c:strCache/c:pt[2]/c:v"_ostr
, "Row 2");
438 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testChartDataTable
)
440 loadFromFile(u
"docx/testChartDataTable.docx");
442 save("Office Open XML Text");
443 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
444 CPPUNIT_ASSERT(pXmlDoc
);
445 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:dTable/c:showHorzBorder"_ostr
, "val"_ostr
, "1");
446 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:dTable/c:showVertBorder"_ostr
, "val"_ostr
, "1");
447 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:dTable/c:showOutline"_ostr
, "val"_ostr
, "1");
450 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testChartExternalData
)
452 loadFromFile(u
"docx/testMultipleChart.docx");
454 save("Office Open XML Text");
455 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
456 CPPUNIT_ASSERT(pXmlDoc
);
457 assertXPath(pXmlDoc
, "/c:chartSpace/c:externalData"_ostr
);
460 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testEmbeddingsGrabBag
)
462 // The problem was that .xlsx files were missing from docx file from embeddings folder
463 // after saving file.
464 // This test case tests whether embeddings files grabbagged properly in correct object.
466 loadFromFile(u
"docx/testMultiplechartembeddings.docx" );
467 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
468 uno::Reference
<beans::XPropertySet
> xTextDocumentPropertySet(xTextDocument
, uno::UNO_QUERY
);
469 uno::Sequence
<beans::PropertyValue
> aGrabBag(0);
470 xTextDocumentPropertySet
->getPropertyValue("InteropGrabBag") >>= aGrabBag
;
471 CPPUNIT_ASSERT(aGrabBag
.hasElements()); // Grab Bag not empty
472 bool bEmbeddings
= false;
473 const char* const testEmbeddedFileNames
[] {"word/embeddings/Microsoft_Excel_Worksheet3.xlsx",
474 "word/embeddings/Microsoft_Excel_Worksheet2.xlsx",
475 "word/embeddings/Microsoft_Excel_Worksheet1.xlsx"};
476 for(beans::PropertyValue
const & prop
: std::as_const(aGrabBag
))
478 if (prop
.Name
== "OOXEmbeddings")
481 uno::Sequence
<beans::PropertyValue
> aEmbeddingsList(0);
482 uno::Reference
<io::XInputStream
> aEmbeddingXlsxStream
;
483 OUString aEmbeddedfileName
;
484 CPPUNIT_ASSERT(prop
.Value
>>= aEmbeddingsList
); // PropertyValue of proper type
485 sal_Int32 length
= aEmbeddingsList
.getLength();
486 CPPUNIT_ASSERT_EQUAL(sal_Int32(3), length
);
487 for(int j
= 0; j
< length
; ++j
)
489 aEmbeddingsList
[j
].Value
>>= aEmbeddingXlsxStream
;
490 aEmbeddedfileName
= aEmbeddingsList
[j
].Name
;
491 CPPUNIT_ASSERT(aEmbeddingXlsxStream
); // Reference not empty
492 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(testEmbeddedFileNames
[j
]),aEmbeddedfileName
);
496 CPPUNIT_ASSERT(bEmbeddings
); // Grab Bag has all the expected elements
499 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testAreaChartLoad
)
501 loadFromFile(u
"docx/testAreaChartLoad.docx");
503 // FIXME: validation error in OOXML export: Errors: 1
506 save("Office Open XML Text");
507 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
508 CPPUNIT_ASSERT(pXmlDoc
);
509 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:areaChart/c:ser/c:dLbls/c:showVal"_ostr
, "val"_ostr
, "1");
510 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:areaChart/c:ser/c:dLbls/c:dLbl"_ostr
, 0);
513 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testUpDownBars
)
515 loadFromFile(u
"docx/UpDownBars.docx");
516 save("Office Open XML Text");
517 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
518 CPPUNIT_ASSERT(pXmlDoc
);
519 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:upDownBars"_ostr
, 0);
522 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDoughnutChart
)
524 loadFromFile(u
"docx/doughnutChart.docx");
525 save("Office Open XML Text");
526 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
527 CPPUNIT_ASSERT(pXmlDoc
);
529 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:doughnutChart"_ostr
);
532 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDisplayUnits
)
534 loadFromFile(u
"docx/DisplayUnits.docx");
535 save("Office Open XML Text");
536 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
537 CPPUNIT_ASSERT(pXmlDoc
);
539 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:valAx/c:dispUnits/c:builtInUnit"_ostr
, "val"_ostr
, "billions");
542 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testFdo74115WallGradientFill
)
544 loadFromFile(u
"docx/fdo74115_WallGradientFill.docx");
545 save("Office Open XML Text");
546 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
547 CPPUNIT_ASSERT(pXmlDoc
);
549 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:spPr/a:gradFill"_ostr
);
552 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testFdo74115WallBitmapFill
)
554 loadFromFile(u
"docx/fdo74115_WallBitmapFill.docx");
555 save("Office Open XML Text");
556 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
557 CPPUNIT_ASSERT(pXmlDoc
);
558 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:spPr/a:blipFill"_ostr
);
561 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testPieChartWallLineStyle
)
563 loadFromFile(u
"odt/testPieChartWallLineStyle.odt");
565 // FIXME: validation error in OOXML export: Errors: 9
568 save("Office Open XML Text");
569 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
570 CPPUNIT_ASSERT(pXmlDoc
);
571 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:spPr/a:ln/a:noFill"_ostr
);
574 //The below test case tests the built in marker 'x' for Office 2010 in Line charts
576 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testFdo78290LineChartMarkerX
)
578 loadFromFile(u
"docx/fdo78290_Line_Chart_Marker_x.docx");
579 save("Office Open XML Text");
580 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
581 CPPUNIT_ASSERT(pXmlDoc
);
582 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:lineChart[1]/c:ser[1]/c:marker[1]/c:symbol[1]"_ostr
,"val"_ostr
,"x");
583 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:lineChart[1]/c:ser[1]/c:marker[1]/c:size[1]"_ostr
,"val"_ostr
,"7");
586 // We can also use the built in marker 'x' in scatter chart, hence writing the test case for the same.
588 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testFdo78290ScatterChartMarkerX
)
590 loadFromFile(u
"docx/fdo78290_Scatter_Chart_Marker_x.docx");
591 save("Office Open XML Text");
592 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
593 CPPUNIT_ASSERT(pXmlDoc
);
594 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:scatterChart[1]/c:ser[1]/c:marker[1]/c:symbol[1]"_ostr
,"val"_ostr
,"x");
595 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:scatterChart[1]/c:ser[1]/c:marker[1]/c:size[1]"_ostr
,"val"_ostr
,"7");
598 // Also in a combination of charts like a column chart and line chart, we can use the built in marker 'x'
599 // for the line chart too. hence put a test case for the combination chart also.
601 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testFdo78290CombinationChartMarkerX
)
603 loadFromFile(u
"docx/fdo78290_Combination_Chart_Marker_x.docx");
604 save("Office Open XML Text");
605 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
606 CPPUNIT_ASSERT(pXmlDoc
);
607 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:lineChart[1]/c:ser[1]/c:marker[1]/c:symbol[1]"_ostr
,"val"_ostr
,"x");
608 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:lineChart[1]/c:ser[1]/c:marker[1]/c:size[1]"_ostr
,"val"_ostr
,"7");
611 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testTdf126115IndividualMarker
)
613 // Check individual marker properties.
614 loadFromFile(u
"xlsx/tdf126115.xlsx");
615 save("Calc Office Open XML");
616 xmlDocUniquePtr pXmlDoc
= parseExport("xl/charts/chart1.xml");
617 CPPUNIT_ASSERT(pXmlDoc
);
619 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[1]/c:dPt/c:marker/c:symbol"_ostr
, "val"_ostr
, "square");
620 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[1]/c:dPt/c:marker/c:size"_ostr
, "val"_ostr
, "8");
621 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[1]/c:dPt/c:marker/c:spPr/a:solidFill/a:srgbClr"_ostr
, "val"_ostr
, "ff0000");
623 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[2]/c:dPt/c:marker/c:symbol"_ostr
, "val"_ostr
, "x");
624 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[2]/c:dPt/c:marker/c:size"_ostr
, "val"_ostr
, "15");
625 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[2]/c:dPt/c:marker/c:spPr/a:solidFill/a:srgbClr"_ostr
, "val"_ostr
, "7030a0");
628 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testAxisNumberFormatODS
)
632 void check( const Reference
<chart2::XChartDocument
>& xChartDoc
)
634 Reference
<chart2::XAxis
> xAxisX
= getAxisFromDoc(xChartDoc
, 0, 0, 0);
635 Reference
<chart2::XTitled
> xTitle(xAxisX
, UNO_QUERY_THROW
);
636 OUString aTitleText
= getTitleString(xTitle
);
637 CPPUNIT_ASSERT_EQUAL(OUString("Linked To Source"), aTitleText
);
639 sal_Int32 nNumFmt
= getNumberFormatFromAxis(xAxisX
);
640 sal_Int16 nType
= getNumberFormatType(xChartDoc
, nNumFmt
);
641 CPPUNIT_ASSERT_MESSAGE("X axis should be percentage format.", (nType
& util::NumberFormat::PERCENT
));
643 bool bNumFmtLinked
= false;
644 Reference
<beans::XPropertySet
> xPS(xAxisX
, uno::UNO_QUERY_THROW
);
645 xPS
->getPropertyValue("LinkNumberFormatToSource") >>= bNumFmtLinked
;
646 CPPUNIT_ASSERT_MESSAGE("X axis should have its number format linked to source.", bNumFmtLinked
);
648 Reference
<chart2::XAxis
> xAxisY
= getAxisFromDoc(xChartDoc
, 0, 1, 0);
649 xTitle
.set(xAxisY
, UNO_QUERY_THROW
);
650 aTitleText
= getTitleString(xTitle
);
651 CPPUNIT_ASSERT_EQUAL(OUString("Not Linked"), aTitleText
);
653 nNumFmt
= getNumberFormatFromAxis(xAxisY
);
654 nType
= getNumberFormatType(xChartDoc
, nNumFmt
);
655 CPPUNIT_ASSERT_MESSAGE("Y axis should be a normal number format.", (nType
& util::NumberFormat::NUMBER
));
657 bNumFmtLinked
= true;
658 xPS
.set(xAxisY
, uno::UNO_QUERY_THROW
);
659 xPS
->getPropertyValue("LinkNumberFormatToSource") >>= bNumFmtLinked
;
660 CPPUNIT_ASSERT_MESSAGE("Y axis should not have its number format linked to source.", !bNumFmtLinked
);
665 loadFromFile(u
"ods/axis-numformats-linked.ods");
667 Reference
<chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet(0, mxComponent
);
668 aTest
.check(xChartDoc
);
670 // Reload the document and make sure everything remains intact.
671 saveAndReload("calc8");
672 xChartDoc
= getChartDocFromSheet(0, mxComponent
);
673 aTest
.check(xChartDoc
);
676 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testAxisNumberFormatXLS
)
680 void check( const Reference
<chart2::XChartDocument
>& xChartDoc
, bool bNumFmtLinkedActual
, sal_Int16 nNumFmtTypeFlag
) const
682 Reference
<chart2::XAxis
> xAxisY
= getAxisFromDoc( xChartDoc
, 0, 1, 0 );
683 bool bNumFmtLinked
= false;
684 Reference
<beans::XPropertySet
> xPS( xAxisY
, uno::UNO_QUERY_THROW
);
685 xPS
->getPropertyValue( "LinkNumberFormatToSource" ) >>= bNumFmtLinked
;
687 if ( bNumFmtLinkedActual
)
688 CPPUNIT_ASSERT_MESSAGE( "Y axis should have its number format linked to source.", bNumFmtLinked
);
691 CPPUNIT_ASSERT_MESSAGE( "Y axis should not have its number format linked to source.", !bNumFmtLinked
);
693 sal_Int32 nNumFmt
= getNumberFormatFromAxis( xAxisY
);
694 sal_Int16 nType
= getNumberFormatType( xChartDoc
, nNumFmt
);
695 if ( nNumFmtTypeFlag
== util::NumberFormat::PERCENT
)
696 CPPUNIT_ASSERT_MESSAGE( "Y axis should be percentage format.", ( nType
& util::NumberFormat::PERCENT
) );
698 CPPUNIT_ASSERT_MESSAGE( "Y axis should be number format.", ( nType
& util::NumberFormat::NUMBER
) );
702 void change( const Reference
<chart2::XChartDocument
>& xChartDoc
, sal_Int16 nNumFmtTypeFlag
)
704 Reference
<chart2::XAxis
> xAxisY
= getAxisFromDoc( xChartDoc
, 0, 1, 0 );
705 Reference
<beans::XPropertySet
> xPS( xAxisY
, uno::UNO_QUERY_THROW
);
707 xPS
->setPropertyValue( "LinkNumberFormatToSource", aAny
);
709 Reference
<util::XNumberFormatsSupplier
> xNFS( xChartDoc
, uno::UNO_QUERY_THROW
);
710 Reference
<util::XNumberFormats
> xNumberFormats
= xNFS
->getNumberFormats();
711 CPPUNIT_ASSERT( xNumberFormats
.is() );
712 lang::Locale aLocale
{ "en", "US", "" };
713 Sequence
<sal_Int32
> aNumFmts
= xNumberFormats
->queryKeys( nNumFmtTypeFlag
, aLocale
, false );
714 CPPUNIT_ASSERT( aNumFmts
.hasElements() );
715 aAny
<<= aNumFmts
[0];
716 xPS
->setPropertyValue( CHART_UNONAME_NUMFMT
, aAny
);
721 loadFromFile(u
"xls/axis_sourceformatting.xls" );
723 Reference
<chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
724 aTest
.check( xChartDoc
, true, util::NumberFormat::PERCENT
);
726 aTest
.change( xChartDoc
, util::NumberFormat::NUMBER
);
727 // Write the document(xls) with changes made close it, load it and check if changes are intact
728 saveAndReload( "MS Excel 97" );
729 xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
730 aTest
.check( xChartDoc
, false, util::NumberFormat::NUMBER
);
733 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabelBordersDOCX
)
738 css::drawing::LineStyle meStyle
;
745 * Chart 1 has 4 bars of which 1st and 3rd have labels with borders
748 void checkObject1( const Reference
<chart2::XChartDocument
>& xChartDoc
)
750 CPPUNIT_ASSERT(xChartDoc
.is());
752 Reference
<chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc(xChartDoc
, 0);
753 CPPUNIT_ASSERT(xDataSeries
.is());
755 // Check to make sure that data points 0 and 2 have local properties.
756 Reference
<beans::XPropertySet
> xPropSet(xDataSeries
, uno::UNO_QUERY
);
757 CPPUNIT_ASSERT(xPropSet
.is());
759 Sequence
<sal_Int32
> aIndices
;
760 xPropSet
->getPropertyValue("AttributedDataPoints") >>= aIndices
;
762 CPPUNIT_ASSERT_EQUAL_MESSAGE("There should be 2 data points with local properties.", sal_Int32(2), aIndices.getLength());
763 CPPUNIT_ASSERT_EQUAL(sal_Int32(0), aIndices[0]);
764 CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aIndices[1]);
767 static const Check aDataPoints
[] =
769 { 0, css::drawing::LineStyle_SOLID
, 0x00FFFF00 }, // solid yellow
770 { 2, css::drawing::LineStyle_SOLID
, 0x00FF0000 } // solid red
773 for (size_t i
= 0; i
< std::size(aDataPoints
); ++i
)
775 xPropSet
= xDataSeries
->getDataPointByIndex(aDataPoints
[i
].mnIndex
);
776 CPPUNIT_ASSERT(xPropSet
.is());
778 css::drawing::LineStyle eLineStyle
= css::drawing::LineStyle_NONE
;
779 xPropSet
->getPropertyValue(CHART_UNONAME_LABEL_BORDER_STYLE
) >>= eLineStyle
;
780 CPPUNIT_ASSERT_EQUAL(aDataPoints
[i
].meStyle
, eLineStyle
);
782 sal_Int32 nWidth
= -1;
783 xPropSet
->getPropertyValue(CHART_UNONAME_LABEL_BORDER_WIDTH
) >>= nWidth
;
784 CPPUNIT_ASSERT(nWidth
> 0);
787 xPropSet
->getPropertyValue(CHART_UNONAME_LABEL_BORDER_COLOR
) >>= nColor
;
788 CPPUNIT_ASSERT_EQUAL_MESSAGE("Border color is wrong.", aDataPoints
[i
].mnColor
, nColor
);
793 * Chart 2 has all its data labels with identical borders.
795 void checkObject2( const Reference
<chart2::XChartDocument
>& xChartDoc
)
797 CPPUNIT_ASSERT(xChartDoc
.is());
799 Reference
<chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc(xChartDoc
, 0);
800 CPPUNIT_ASSERT(xDataSeries
.is());
802 Reference
<beans::XPropertySet
> xPropSet(xDataSeries
, uno::UNO_QUERY
);
803 CPPUNIT_ASSERT(xPropSet
.is());
805 css::drawing::LineStyle eLineStyle
= css::drawing::LineStyle_NONE
;
806 xPropSet
->getPropertyValue(CHART_UNONAME_LABEL_BORDER_STYLE
) >>= eLineStyle
;
807 CPPUNIT_ASSERT_EQUAL(css::drawing::LineStyle_SOLID
, eLineStyle
);
809 sal_Int32 nWidth
= -1;
810 xPropSet
->getPropertyValue(CHART_UNONAME_LABEL_BORDER_WIDTH
) >>= nWidth
;
811 CPPUNIT_ASSERT(nWidth
> 0);
814 xPropSet
->getPropertyValue(CHART_UNONAME_LABEL_BORDER_COLOR
) >>= nColor
;
815 CPPUNIT_ASSERT_EQUAL_MESSAGE("Border color should be green.", COL_LIGHTGREEN
, nColor
);
820 loadFromFile(u
"docx/data-label-borders.docx");
822 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
824 // "Automatic" chart background fill in docx should be loaded as solid white.
825 Reference
<beans::XPropertySet
> xPropSet
= xChartDoc
->getPageBackground();
826 CPPUNIT_ASSERT(xPropSet
.is());
827 drawing::FillStyle eStyle
= xPropSet
->getPropertyValue("FillStyle").get
<drawing::FillStyle
>();
828 sal_Int32 nColor
= xPropSet
->getPropertyValue("FillColor").get
<sal_Int32
>();
829 CPPUNIT_ASSERT_EQUAL_MESSAGE("'Automatic' chart background fill in docx should be loaded as solid fill.",
830 drawing::FillStyle_SOLID
, eStyle
);
831 CPPUNIT_ASSERT_EQUAL_MESSAGE("'Automatic' chart background fill in docx should be loaded as solid white.",
832 sal_Int32(0x00FFFFFF), sal_Int32(nColor
& 0x00FFFFFF)); // highest 2 bytes are transparency which we ignore here.
834 aTest
.checkObject1(xChartDoc
);
835 xChartDoc
.set(getChartDocFromWriter(1), uno::UNO_QUERY
);
836 aTest
.checkObject2(xChartDoc
);
838 // FIXME: validation error in OOXML export: Errors: 3
841 saveAndReload("Office Open XML Text");
843 xChartDoc
.set(getChartDocFromWriter(0), uno::UNO_QUERY
);
844 aTest
.checkObject1(xChartDoc
);
845 xChartDoc
.set(getChartDocFromWriter(1), uno::UNO_QUERY
);
846 aTest
.checkObject2(xChartDoc
);
849 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabel3DChartDOCX
)
851 loadFromFile(u
"docx/3d-bar-label.docx");
853 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
854 CPPUNIT_ASSERT(xChartDoc
.is());
856 // FIXME: validation error in OOXML export: Errors: 1
859 save("Office Open XML Text");
860 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
861 CPPUNIT_ASSERT(pXmlDoc
);
863 // We must not export label position attributes for 3D bar charts. The
864 // same rule also applies to several other 3D charts, apparently.
865 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dLbls/c:dLblPos"_ostr
, 0);
866 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:bar3DChart/c:ser/c:dLbls/c:dLbl/c:dLblPos"_ostr
, 0);
869 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabelBarChartDOCX
)
871 loadFromFile(u
"docx/bar-chart-labels.docx");
873 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
874 CPPUNIT_ASSERT(xChartDoc
.is());
876 save("Office Open XML Text");
877 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
878 CPPUNIT_ASSERT(pXmlDoc
);
880 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser[1]/c:dLbls/c:dLblPos"_ostr
, "val"_ostr
, "ctr");
881 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser[2]/c:dLbls/c:dLblPos"_ostr
, "val"_ostr
, "inEnd");
882 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser[3]/c:dLbls/c:dLblPos"_ostr
, "val"_ostr
, "inBase");
885 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabelClusteredBarChartDOCX
)
887 loadFromFile(u
"docx/clustered-bar-chart-labels.docx");
889 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
890 CPPUNIT_ASSERT(xChartDoc
.is());
892 // FIXME: validation error in OOXML export: Errors: 9
895 save("Office Open XML Text");
896 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
897 CPPUNIT_ASSERT(pXmlDoc
);
899 // This was "t", should be one of the allowed values.
900 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:barChart/c:ser[1]/c:dLbls/c:dLbl[2]/c:dLblPos"_ostr
, "val"_ostr
, "outEnd");
903 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabelRadarChartDOCX
)
905 loadFromFile(u
"docx/radar-chart-labels.docx");
907 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
908 CPPUNIT_ASSERT(xChartDoc
.is());
910 save("Office Open XML Text");
911 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
912 CPPUNIT_ASSERT(pXmlDoc
);
914 // We must not export label position attributes for radar charts.
915 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:radarChart/c:ser/c:dLbls/c:dLblPos"_ostr
, 0);
916 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:radarChart/c:ser/c:dLbls/c:dLbl/c:dLblPos"_ostr
, 0);
919 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabelDoughnutChartDOCX
)
921 loadFromFile(u
"docx/doughnut-chart-labels.docx");
923 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
924 CPPUNIT_ASSERT(xChartDoc
.is());
926 // FIXME: validation error in OOXML export: Errors: 1
929 save("Office Open XML Text");
930 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
931 CPPUNIT_ASSERT(pXmlDoc
);
933 // We must not export label position attributes for doughnut charts.
934 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:doughnutChart/c:ser/c:dLbls/c:dLblPos"_ostr
, 0);
935 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:doughnutChart/c:ser/c:dLbls/c:dLbl/c:dLblPos"_ostr
, 0);
938 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabelAreaChartDOCX
)
940 loadFromFile(u
"docx/area-chart-labels.docx");
942 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
943 CPPUNIT_ASSERT(xChartDoc
.is());
945 // FIXME: validation error in OOXML export: Errors: 1
948 save("Office Open XML Text");
949 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
950 CPPUNIT_ASSERT(pXmlDoc
);
952 // We must not export label position attributes for area charts.
953 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:areaChart/c:ser/c:dLbls/c:dLblPos"_ostr
, 0);
954 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:areaChart/c:ser/c:dLbls/c:dLbl/c:dLblPos"_ostr
, 0);
957 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testDataLabelDefaultLineChartDOCX
)
959 // This file was created by Word 2007, which doesn't provide default data
960 // label position (2010 does). Make sure its default data label position
961 // is RIGHT when exporting.
963 loadFromFile(u
"docx/line-chart-label-default-placement.docx");
965 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
966 CPPUNIT_ASSERT(xChartDoc
.is());
968 saveAndReload("Office Open XML Text");
970 xChartDoc
.set(getChartDocFromWriter(0), uno::UNO_QUERY
);
971 Reference
<chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc(xChartDoc
, 0);
972 Reference
<beans::XPropertySet
> xPropSet(xDataSeries
, uno::UNO_QUERY
);
973 CPPUNIT_ASSERT(xPropSet
.is());
974 sal_Int32 nLabelPlacement
= -1;
975 if (xPropSet
->getPropertyValue("LabelPlacement") >>= nLabelPlacement
)
976 // This option may not be set. Check its value only when it's set.
977 CPPUNIT_ASSERT_EQUAL_MESSAGE("Line chart's default label placement should be 'right'.", chart::DataLabelPlacement::RIGHT
, nLabelPlacement
);
980 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testIndividualDataLabelProps
)
982 loadFromFile(u
"xlsx/tdf122915.xlsx");
984 // FIXME: validation error in OOXML export: Errors: 1
987 save("Calc Office Open XML");
988 xmlDocUniquePtr pXmlDoc
= parseExport("xl/charts/chart1.xml");
989 CPPUNIT_ASSERT(pXmlDoc
);
990 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[3]/c:dLbls/c:dLbl/c:txPr/a:p/a:pPr/a:defRPr"_ostr
, "b"_ostr
, "1");
991 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[3]/c:dLbls/c:dLbl/c:txPr/a:p/a:pPr/a:defRPr"_ostr
, "sz"_ostr
, "1600");
992 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[3]/c:dLbls/c:dLbl/c:txPr/a:p/a:pPr/a:defRPr/a:solidFill/a:srgbClr"_ostr
, "val"_ostr
, "ff0000");
993 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:scatterChart/c:ser[3]/c:dLbls/c:dLbl/c:txPr/a:p/a:pPr/a:defRPr/a:latin"_ostr
, "typeface"_ostr
, "Times New Roman");
996 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testBarChartRotation
)
998 loadFromFile(u
"docx/barChartRotation.docx");
999 save("Office Open XML Text");
1000 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
1001 CPPUNIT_ASSERT(pXmlDoc
);
1003 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:view3D/c:rotX"_ostr
, "val"_ostr
, "30");
1004 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:view3D/c:rotY"_ostr
, "val"_ostr
, "50");
1007 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testShapeFollowedByChart
)
1009 /* If there is a scenario where a chart is followed by a shape
1010 which is being exported as an alternate content then, the
1011 docPr Id is being repeated, ECMA 20.4.2.5 says that the
1012 docPr Id should be unique, ensuring the same here.
1014 loadFromFile(u
"docx/FDO74430.docx");
1016 // FIXME: validation error in OOXML export: Errors: 5
1019 save("Office Open XML Text" );
1020 xmlDocUniquePtr pXmlDoc
= parseExport("word/document.xml");
1021 CPPUNIT_ASSERT(pXmlDoc
);
1023 OUString aValueOfFirstDocPR
= getXPath(pXmlDoc
, "/w:document/w:body/w:p[3]/w:r[1]/w:drawing[1]/wp:inline[1]/wp:docPr[1]"_ostr
, "id"_ostr
);
1024 OUString aValueOfSecondDocPR
= getXPath(pXmlDoc
, "/w:document/w:body/w:p[3]/w:r[1]/mc:AlternateContent[1]/mc:Choice[1]/w:drawing[1]/wp:anchor[1]/wp:docPr[1]"_ostr
, "id"_ostr
);
1026 CPPUNIT_ASSERT( aValueOfFirstDocPR
!= aValueOfSecondDocPR
);
1029 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testPieChartDataLabels
)
1031 loadFromFile(u
"docx/PieChartDataLabels.docx");
1033 // FIXME: validation error in OOXML export: Errors: 19
1036 save("Office Open XML Text");
1037 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
1038 CPPUNIT_ASSERT(pXmlDoc
);
1039 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:pie3DChart/c:ser[1]/c:dLbls/c:dLbl[1]/c:dLblPos"_ostr
, "val"_ostr
, "outEnd");
1042 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testSeriesIdxOrder
)
1044 loadFromFile(u
"docx/testSeriesIdxOrder.docx");
1045 save("Office Open XML Text");
1046 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
1047 CPPUNIT_ASSERT(pXmlDoc
);
1048 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:lineChart[1]/c:ser[1]/c:idx[1]"_ostr
, "val"_ostr
, "1");
1049 assertXPath(pXmlDoc
, "/c:chartSpace[1]/c:chart[1]/c:plotArea[1]/c:lineChart[1]/c:ser[1]/c:order[1]"_ostr
, "val"_ostr
, "1");
1052 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testScatterPlotLabels
)
1054 loadFromFile(u
"odt/scatter-plot-labels.odt");
1055 Reference
<chart2::XChartDocument
> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY
);
1056 CPPUNIT_ASSERT(xChartDoc
.is());
1058 Reference
<chart2::XChartType
> xCT
= getChartTypeFromDoc(xChartDoc
, 0);
1059 CPPUNIT_ASSERT(xCT
.is());
1061 // Make sure the original chart has 'a', 'b', 'c' as its data labels.
1062 std::vector
<uno::Sequence
<uno::Any
> > aLabels
= getDataSeriesLabelsFromChartType(xCT
);
1063 CPPUNIT_ASSERT_EQUAL(size_t(3), aLabels
.size());
1064 CPPUNIT_ASSERT_EQUAL(OUString("a"), aLabels
[0][0].get
<OUString
>());
1065 CPPUNIT_ASSERT_EQUAL(OUString("b"), aLabels
[1][0].get
<OUString
>());
1066 CPPUNIT_ASSERT_EQUAL(OUString("c"), aLabels
[2][0].get
<OUString
>());
1068 // Reload the doc and check again. The labels should not change.
1069 saveAndReload("writer8");
1071 xChartDoc
.set(getChartDocFromWriter(0), uno::UNO_QUERY
);
1072 CPPUNIT_ASSERT(xChartDoc
.is());
1074 xCT
= getChartTypeFromDoc(xChartDoc
, 0);
1075 CPPUNIT_ASSERT(xCT
.is());
1077 aLabels
= getDataSeriesLabelsFromChartType(xCT
);
1078 CPPUNIT_ASSERT_EQUAL(size_t(3), aLabels
.size());
1079 CPPUNIT_ASSERT_EQUAL(OUString("a"), aLabels
[0][0].get
<OUString
>());
1080 CPPUNIT_ASSERT_EQUAL(OUString("b"), aLabels
[1][0].get
<OUString
>());
1081 CPPUNIT_ASSERT_EQUAL(OUString("c"), aLabels
[2][0].get
<OUString
>());
1084 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testErrorBarDataRangeODS
)
1086 loadFromFile(u
"ods/ErrorBarRange.ods");
1087 saveAndReload("calc8");
1089 uno::Reference
< chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
1090 CPPUNIT_ASSERT(xChartDoc
.is());
1092 Reference
< chart2::XDataSeries
> xDataSeries
= getDataSeriesFromDoc( xChartDoc
, 0 );
1093 CPPUNIT_ASSERT( xDataSeries
.is() );
1095 Reference
< beans::XPropertySet
> xPropSet( xDataSeries
, UNO_QUERY_THROW
);
1097 // test that y error bars are there
1098 Reference
< beans::XPropertySet
> xErrorBarYProps
;
1099 xPropSet
->getPropertyValue(CHART_UNONAME_ERRORBAR_Y
) >>= xErrorBarYProps
;
1100 uno::Any aAny
= xErrorBarYProps
->getPropertyValue("ErrorBarRangePositive");
1101 CPPUNIT_ASSERT(aAny
.hasValue());
1104 CPPUNIT_ASSERT_EQUAL(OUString("$Sheet1.$B$1:$B$3"), aPosRange
);
1106 aAny
= xErrorBarYProps
->getPropertyValue("ErrorBarRangeNegative");
1107 CPPUNIT_ASSERT(aAny
.hasValue());
1110 CPPUNIT_ASSERT_EQUAL(OUString("$Sheet1.$C$1:$C$3"), aNegRange
);
1113 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testChartCrash
)
1115 loadFromFile(u
"docx/FDO75975.docx");
1116 save("Office Open XML Text");
1117 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
1118 CPPUNIT_ASSERT(pXmlDoc
);
1121 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testPieChartRotation
)
1123 loadFromFile(u
"docx/pieChartRotation.docx");
1124 save("Office Open XML Text");
1125 xmlDocUniquePtr pXmlDoc
= parseExport("word/charts/chart1.xml");
1126 CPPUNIT_ASSERT(pXmlDoc
);
1127 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:view3D/c:rotX"_ostr
, "val"_ostr
, "40");
1128 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:view3D/c:rotY"_ostr
, "val"_ostr
, "30");
1131 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testEmbeddingsOleObjectGrabBag
)
1133 // The problem was that .bin files were missing from docx file from embeddings folder
1134 // after saving file.
1135 // This test case tests whether embeddings files grabbagged properly in correct object.
1137 loadFromFile(u
"docx/testchartoleobjectembeddings.docx" );
1138 uno::Reference
<text::XTextDocument
> xTextDocument(mxComponent
, uno::UNO_QUERY
);
1139 uno::Reference
<beans::XPropertySet
> xTextDocumentPropertySet(xTextDocument
, uno::UNO_QUERY
);
1140 uno::Sequence
<beans::PropertyValue
> aGrabBag(0);
1141 xTextDocumentPropertySet
->getPropertyValue("InteropGrabBag") >>= aGrabBag
;
1142 CPPUNIT_ASSERT(aGrabBag
.hasElements()); // Grab Bag not empty
1143 bool bEmbeddings
= false;
1144 const char* const testEmbeddedFileNames
[] = {"word/embeddings/oleObject1.bin"};
1145 for(beans::PropertyValue
const & prop
: std::as_const(aGrabBag
))
1147 if (prop
.Name
== "OOXEmbeddings")
1150 uno::Sequence
<beans::PropertyValue
> aEmbeddingsList(0);
1151 uno::Reference
<io::XInputStream
> aEmbeddingXlsxStream
;
1152 OUString aEmbeddedfileName
;
1153 CPPUNIT_ASSERT(prop
.Value
>>= aEmbeddingsList
); // PropertyValue of proper type
1154 sal_Int32 length
= aEmbeddingsList
.getLength();
1155 CPPUNIT_ASSERT_EQUAL(sal_Int32(1), length
);
1156 for(int j
= 0; j
< length
; ++j
)
1158 aEmbeddingsList
[j
].Value
>>= aEmbeddingXlsxStream
;
1159 aEmbeddedfileName
= aEmbeddingsList
[j
].Name
;
1160 CPPUNIT_ASSERT(aEmbeddingXlsxStream
); // Reference not empty
1161 CPPUNIT_ASSERT_EQUAL(OUString::createFromAscii(testEmbeddedFileNames
[j
]),aEmbeddedfileName
);
1165 CPPUNIT_ASSERT(bEmbeddings
); // Grab Bag has all the expected elements
1170 void checkGapWidth(Reference
<beans::XPropertySet
> const & xPropSet
, sal_Int32 nValue
)
1172 uno::Any aAny
= xPropSet
->getPropertyValue("GapwidthSequence");
1173 CPPUNIT_ASSERT(aAny
.hasValue());
1174 uno::Sequence
< sal_Int32
> aSequence
;
1176 CPPUNIT_ASSERT(aSequence
.hasElements());
1177 CPPUNIT_ASSERT_EQUAL(nValue
, aSequence
[0]);
1180 void checkOverlap(Reference
<beans::XPropertySet
> const & xPropSet
, sal_Int32 nValue
)
1182 uno::Any aAny
= xPropSet
->getPropertyValue("OverlapSequence");
1183 CPPUNIT_ASSERT(aAny
.hasValue());
1184 uno::Sequence
< sal_Int32
> aSequence
;
1186 CPPUNIT_ASSERT(aSequence
.hasElements());
1187 CPPUNIT_ASSERT_EQUAL(nValue
, aSequence
[0]);
1190 void checkSheetForGapWidthAndOverlap(uno::Reference
< chart2::XChartDocument
> const & xChartDoc
,
1191 sal_Int32 nExpectedGapWidth
, sal_Int32 nExpectedOverlap
)
1193 CPPUNIT_ASSERT(xChartDoc
.is());
1195 Reference
< chart2::XChartType
> xChartType
= getChartTypeFromDoc( xChartDoc
, 0 );
1196 CPPUNIT_ASSERT(xChartType
.is());
1198 Reference
< beans::XPropertySet
> xPropSet( xChartType
, uno::UNO_QUERY_THROW
);
1199 checkGapWidth(xPropSet
, nExpectedGapWidth
);
1200 checkOverlap(xPropSet
, nExpectedOverlap
);
1206 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testGapWidthXLSX
)
1208 loadFromFile(u
"xlsx/gapWidth.xlsx");
1210 uno::Reference
< chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
1211 checkSheetForGapWidthAndOverlap(xChartDoc
, 120, -60);
1213 xChartDoc
= getChartDocFromSheet( 1, mxComponent
);
1214 checkSheetForGapWidthAndOverlap(xChartDoc
, 50, 30);
1216 saveAndReload("Calc Office Open XML");
1218 xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
1219 checkSheetForGapWidthAndOverlap(xChartDoc
, 120, -60);
1221 xChartDoc
= getChartDocFromSheet( 1, mxComponent
);
1222 checkSheetForGapWidthAndOverlap(xChartDoc
, 50, 30);
1225 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testSmoothedLines
)
1227 loadFromFile(u
"ods/smoothedLines.ods");
1228 save("Calc Office Open XML");
1229 xmlDocUniquePtr pXmlDoc
= parseExport("xl/charts/chart1.xml");
1230 CPPUNIT_ASSERT(pXmlDoc
);
1231 assertXPath(pXmlDoc
, "/c:chartSpace/c:chart/c:plotArea/c:lineChart/c:ser[1]/c:smooth"_ostr
, "val"_ostr
, "0");
1234 CPPUNIT_TEST_FIXTURE(Chart2ExportTest
, testLabelStringODS
)
1236 loadFromFile(u
"ods/labelString.ods");
1238 uno::Reference
< chart2::XChartDocument
> xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
1239 Reference
< chart2::data::XDataSequence
> xLabelSeq
=
1240 getLabelDataSequenceFromDoc(xChartDoc
);
1241 CPPUNIT_ASSERT(xLabelSeq
.is());
1243 OUString aLabelString
= xLabelSeq
->getSourceRangeRepresentation();
1244 CPPUNIT_ASSERT_EQUAL(OUString("\"LabelName\""), aLabelString
);
1246 saveAndReload("calc8");
1248 xChartDoc
= getChartDocFromSheet( 0, mxComponent
);
1249 xLabelSeq
= getLabelDataSequenceFromDoc(xChartDoc
);
1250 CPPUNIT_ASSERT(xLabelSeq
.is());
1252 aLabelString
= xLabelSeq
->getSourceRangeRepresentation();
1253 CPPUNIT_ASSERT_EQUAL(OUString("\"LabelName\""), aLabelString
);
1256 CPPUNIT_PLUGIN_IMPLEMENT();
1258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */