1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sax/tools/converter.hxx>
21 
22 #include "SchXMLAxisContext.hxx"
23 #include "SchXMLChartContext.hxx"
24 #include "SchXMLTools.hxx"
25 #include <xmloff/xmlimp.hxx>
26 #include <xmloff/xmlnamespace.hxx>
27 #include <xmloff/xmlement.hxx>
28 #include <xmloff/xmlstyle.hxx>
29 #include <xmloff/prstylei.hxx>
30 #include <xmloff/namespacemap.hxx>
31 #include <xmloff/xmluconv.hxx>
32 
33 #include <rtl/math.hxx>
34 #include <tools/color.hxx>
35 #include <sal/log.hxx>
36 
37 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
38 #include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
39 #include <com/sun/star/chart/ChartAxisPosition.hpp>
40 #include <com/sun/star/chart/ChartAxisType.hpp>
41 #include <com/sun/star/chart/TimeIncrement.hpp>
42 #include <com/sun/star/chart/TimeInterval.hpp>
43 #include <com/sun/star/chart/TimeUnit.hpp>
44 #include <com/sun/star/chart/XAxis.hpp>
45 #include <com/sun/star/chart/XAxisSupplier.hpp>
46 #include <com/sun/star/chart/XChartDocument.hpp>
47 #include <com/sun/star/chart2/AxisType.hpp>
48 #include <com/sun/star/chart2/XChartDocument.hpp>
49 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
50 
51 #include <com/sun/star/drawing/LineStyle.hpp>
52 
53 using namespace ::xmloff::token;
54 using namespace com::sun::star;
55 
56 using com::sun::star::uno::Reference;
57 
58 const SvXMLEnumMapEntry<SchXMLAxisDimension> aXMLAxisDimensionMap[] =
59 {
60     { XML_X,  SCH_XML_AXIS_X  },
61     { XML_Y,  SCH_XML_AXIS_Y  },
62     { XML_Z,  SCH_XML_AXIS_Z  },
63     { XML_TOKEN_INVALID, SchXMLAxisDimension(0) }
64 };
65 
66 const SvXMLEnumMapEntry<sal_uInt16> aXMLAxisTypeMap[] =
67 {
68     { XML_AUTO,  css::chart::ChartAxisType::AUTOMATIC },
69     { XML_TEXT,  css::chart::ChartAxisType::CATEGORY },
70     { XML_DATE,  css::chart::ChartAxisType::DATE },
71     { XML_TOKEN_INVALID, 0 }
72 };
73 
74 namespace {
75 
76 class SchXMLCategoriesContext : public SvXMLImportContext
77 {
78 private:
79     OUString& mrAddress;
80 
81 public:
82     SchXMLCategoriesContext( SvXMLImport& rImport,
83                                    OUString& rAddress );
84     virtual void SAL_CALL startFastElement( sal_Int32 nElement,
85         const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
86 };
87 
88 class DateScaleContext : public SvXMLImportContext
89 {
90 public:
91     DateScaleContext( SvXMLImport& rImport,
92                         const Reference< beans::XPropertySet >& rAxisProps );
93 
94     virtual void SAL_CALL startFastElement( sal_Int32 nElement,
95         const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override;
96 
97 private:
98     Reference< beans::XPropertySet > m_xAxisProps;
99 };
100 
101 }
102 
SchXMLAxisContext(SchXMLImportHelper & rImpHelper,SvXMLImport & rImport,Reference<chart::XDiagram> const & xDiagram,std::vector<SchXMLAxis> & rAxes,OUString & rCategoriesAddress,bool bAddMissingXAxisForNetCharts,bool bAdaptWrongPercentScaleValues,bool bAdaptXAxisOrientationForOld2DBarCharts,bool & rbAxisPositionAttributeImported)103 SchXMLAxisContext::SchXMLAxisContext( SchXMLImportHelper& rImpHelper,
104                                       SvXMLImport& rImport,
105                                       Reference< chart::XDiagram > const & xDiagram,
106                                       std::vector< SchXMLAxis >& rAxes,
107                                       OUString & rCategoriesAddress,
108                                       bool bAddMissingXAxisForNetCharts,
109                                       bool bAdaptWrongPercentScaleValues,
110                                       bool bAdaptXAxisOrientationForOld2DBarCharts,
111                                       bool& rbAxisPositionAttributeImported ) :
112         SvXMLImportContext( rImport ),
113         m_rImportHelper( rImpHelper ),
114         m_xDiagram( xDiagram ),
115         m_rAxes( rAxes ),
116         m_rCategoriesAddress( rCategoriesAddress ),
117         m_nAxisType(chart::ChartAxisType::AUTOMATIC),
118         m_bAxisTypeImported(false),
119         m_bDateScaleImported(false),
120         m_bAddMissingXAxisForNetCharts( bAddMissingXAxisForNetCharts ),
121         m_bAdaptWrongPercentScaleValues( bAdaptWrongPercentScaleValues ),
122         m_bAdaptXAxisOrientationForOld2DBarCharts( bAdaptXAxisOrientationForOld2DBarCharts ),
123         m_rbAxisPositionAttributeImported( rbAxisPositionAttributeImported )
124 {
125 }
126 
~SchXMLAxisContext()127 SchXMLAxisContext::~SchXMLAxisContext()
128 {}
129 
lcl_getChartAxis(const SchXMLAxis & rCurrentAxis,const Reference<chart::XDiagram> & rDiagram)130 static Reference< chart::XAxis > lcl_getChartAxis(const SchXMLAxis& rCurrentAxis, const Reference< chart::XDiagram >& rDiagram )
131 {
132     Reference< chart::XAxis > xAxis;
133     Reference< chart::XAxisSupplier > xAxisSuppl( rDiagram, uno::UNO_QUERY );
134     if( !xAxisSuppl.is() )
135         return xAxis;
136     if( rCurrentAxis.nAxisIndex == 0 )
137         xAxis = xAxisSuppl->getAxis(rCurrentAxis.eDimension);
138     else
139         xAxis = xAxisSuppl->getSecondaryAxis(rCurrentAxis.eDimension);
140     return xAxis;
141 }
142 
143 /* returns a shape for the current axis's title. The property
144    "Has...AxisTitle" is set to "True" to get the shape
145  */
getTitleShape() const146 Reference< drawing::XShape > SchXMLAxisContext::getTitleShape() const
147 {
148     Reference< drawing::XShape > xResult;
149     Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
150     Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
151     if( !xDiaProp.is() || !xAxis.is() )
152         return xResult;
153 
154     OUString aPropName;
155     switch( m_aCurrentAxis.eDimension )
156     {
157         case SCH_XML_AXIS_X:
158             if( m_aCurrentAxis.nAxisIndex == 0 )
159                 aPropName = "HasXAxisTitle";
160             else
161                 aPropName = "HasSecondaryXAxisTitle";
162             break;
163         case SCH_XML_AXIS_Y:
164             if( m_aCurrentAxis.nAxisIndex == 0 )
165                 aPropName = "HasYAxisTitle";
166             else
167                 aPropName = "HasSecondaryYAxisTitle";
168             break;
169         case SCH_XML_AXIS_Z:
170             aPropName = "HasZAxisTitle";
171             break;
172         case SCH_XML_AXIS_UNDEF:
173             SAL_INFO("xmloff.chart", "Invalid axis" );
174             break;
175     }
176     xDiaProp->setPropertyValue( aPropName, uno::makeAny(true) );
177     xResult.set( xAxis->getAxisTitle(), uno::UNO_QUERY );
178     return xResult;
179 }
180 
CreateGrid(const OUString & sAutoStyleName,bool bIsMajor)181 void SchXMLAxisContext::CreateGrid( const OUString& sAutoStyleName, bool bIsMajor )
182 {
183     Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
184     Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
185     if( !xDiaProp.is() || !xAxis.is() )
186         return;
187 
188     OUString aPropName;
189     switch( m_aCurrentAxis.eDimension )
190     {
191         case SCH_XML_AXIS_X:
192             if( bIsMajor )
193                 aPropName = "HasXAxisGrid";
194             else
195                 aPropName = "HasXAxisHelpGrid";
196             break;
197         case SCH_XML_AXIS_Y:
198             if( bIsMajor )
199                 aPropName = "HasYAxisGrid";
200             else
201                 aPropName = "HasYAxisHelpGrid";
202             break;
203         case SCH_XML_AXIS_Z:
204             if( bIsMajor )
205                 aPropName = "HasZAxisGrid";
206             else
207                 aPropName = "HasZAxisHelpGrid";
208             break;
209         case SCH_XML_AXIS_UNDEF:
210             SAL_INFO("xmloff.chart", "Invalid axis" );
211             break;
212     }
213     xDiaProp->setPropertyValue( aPropName, uno::makeAny(true) );
214 
215     Reference< beans::XPropertySet > xGridProp;
216     if( bIsMajor )
217         xGridProp = xAxis->getMajorGrid();
218     else
219         xGridProp = xAxis->getMinorGrid();
220 
221     // set properties
222     if( xGridProp.is())
223     {
224         // the line color is black as default, in the model it is a light gray
225         xGridProp->setPropertyValue("LineColor",
226                                      uno::makeAny( COL_BLACK ));
227         if (!sAutoStyleName.isEmpty())
228             m_rImportHelper.FillAutoStyle(sAutoStyleName, xGridProp);
229     }
230 }
231 
startFastElement(sal_Int32,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)232 void SchXMLAxisContext::startFastElement( sal_Int32 /*nElement*/,
233     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
234 {
235     // parse attributes
236     for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
237     {
238         switch(aIter.getToken())
239         {
240             case XML_ELEMENT(CHART, XML_DIMENSION):
241                 {
242                     SchXMLAxisDimension nEnumVal;
243                     if( SvXMLUnitConverter::convertEnum( nEnumVal, aIter.toView(), aXMLAxisDimensionMap ))
244                         m_aCurrentAxis.eDimension = nEnumVal;
245                 }
246                 break;
247             case XML_ELEMENT(CHART, XML_NAME):
248                 m_aCurrentAxis.aName = aIter.toString();
249                 break;
250             case XML_ELEMENT(CHART, XML_AXIS_TYPE):
251             case XML_ELEMENT(CHART_EXT, XML_AXIS_TYPE):
252                 sal_uInt16 nEnumVal;
253                 if( SvXMLUnitConverter::convertEnum( nEnumVal, aIter.toView(), aXMLAxisTypeMap ))
254                 {
255                     m_nAxisType = nEnumVal;
256                     m_bAxisTypeImported = true;
257                 }
258                 break;
259             case XML_ELEMENT(CHART, XML_STYLE_NAME):
260                 m_aAutoStyleName = aIter.toString();
261                 break;
262             default:
263                 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
264         }
265     }
266 
267     // check for number of axes with same dimension
268     m_aCurrentAxis.nAxisIndex = 0;
269     sal_Int32 nNumOfAxes = m_rAxes.size();
270     for( sal_Int32 nCurrent = 0; nCurrent < nNumOfAxes; nCurrent++ )
271     {
272         if( m_rAxes[ nCurrent ].eDimension == m_aCurrentAxis.eDimension )
273             m_aCurrentAxis.nAxisIndex++;
274     }
275     CreateAxis();
276 }
277 namespace
278 {
279 
lcl_getAxis(const Reference<frame::XModel> & xChartModel,sal_Int32 nDimensionIndex,sal_Int32 nAxisIndex)280 Reference< chart2::XAxis > lcl_getAxis( const Reference< frame::XModel >& xChartModel,
281                                             sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
282 {
283     Reference< chart2::XAxis > xAxis;
284 
285     try
286     {
287         Reference< chart2::XChartDocument > xChart2Document( xChartModel, uno::UNO_QUERY );
288         if( xChart2Document.is() )
289         {
290             Reference< chart2::XDiagram > xDiagram( xChart2Document->getFirstDiagram());
291             Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW );
292             uno::Sequence< Reference< chart2::XCoordinateSystem > >
293                 aCooSysSeq( xCooSysCnt->getCoordinateSystems());
294             sal_Int32 nCooSysIndex = 0;
295             if( nCooSysIndex < aCooSysSeq.getLength() )
296             {
297                 Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIndex] );
298                 if( xCooSys.is() && nDimensionIndex < xCooSys->getDimension() )
299                 {
300                     const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
301                     if( nAxisIndex <= nMaxAxisIndex )
302                         xAxis = xCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex );
303                 }
304             }
305         }
306     }
307     catch( uno::Exception & )
308     {
309         SAL_INFO("xmloff.chart", "Couldn't get axis" );
310     }
311 
312     return xAxis;
313 }
314 
lcl_divideBy100(uno::Any & rDoubleAny)315 bool lcl_divideBy100( uno::Any& rDoubleAny )
316 {
317     bool bChanged = false;
318     double fValue=0.0;
319     if( (rDoubleAny>>=fValue) && (fValue!=0.0) )
320     {
321         fValue/=100.0;
322         rDoubleAny <<= fValue;
323         bChanged = true;
324     }
325     return bChanged;
326 }
327 
lcl_AdaptWrongPercentScaleValues(chart2::ScaleData & rScaleData)328 bool lcl_AdaptWrongPercentScaleValues(chart2::ScaleData& rScaleData)
329 {
330     bool bChanged = lcl_divideBy100( rScaleData.Minimum );
331     bChanged = lcl_divideBy100( rScaleData.Maximum ) || bChanged;
332     bChanged = lcl_divideBy100( rScaleData.Origin ) || bChanged;
333     bChanged = lcl_divideBy100( rScaleData.IncrementData.Distance ) || bChanged;
334     return bChanged;
335 }
336 
337 }//end anonymous namespace
338 
CreateAxis()339 void SchXMLAxisContext::CreateAxis()
340 {
341     m_rAxes.push_back( m_aCurrentAxis );
342 
343     Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY );
344     if( !xDiaProp.is() )
345         return;
346     OUString aPropName;
347     switch( m_aCurrentAxis.eDimension )
348     {
349         case SCH_XML_AXIS_X:
350             if( m_aCurrentAxis.nAxisIndex == 0 )
351                 aPropName = "HasXAxis";
352             else
353                 aPropName = "HasSecondaryXAxis";
354             break;
355         case SCH_XML_AXIS_Y:
356             if( m_aCurrentAxis.nAxisIndex == 0 )
357                 aPropName = "HasYAxis";
358             else
359                 aPropName = "HasSecondaryYAxis";
360             break;
361         case SCH_XML_AXIS_Z:
362             if( m_aCurrentAxis.nAxisIndex == 0 )
363                 aPropName = "HasZAxis";
364             break;
365         case SCH_XML_AXIS_UNDEF:
366             SAL_INFO("xmloff.chart", "Invalid axis" );
367             break;
368     }
369     try
370     {
371         xDiaProp->setPropertyValue( aPropName, uno::makeAny(true) );
372     }
373     catch( beans::UnknownPropertyException & )
374     {
375         SAL_INFO("xmloff.chart", "Couldn't turn on axis" );
376     }
377     if( m_aCurrentAxis.eDimension==SCH_XML_AXIS_Z )
378     {
379         bool bSettingZAxisSucceeded = false;
380         try
381         {
382             xDiaProp->getPropertyValue( aPropName ) >>= bSettingZAxisSucceeded;
383         }
384         catch( beans::UnknownPropertyException & )
385         {
386             SAL_INFO("xmloff.chart", "Couldn't turn on z axis" );
387         }
388         if( !bSettingZAxisSucceeded )
389             return;
390     }
391 
392     m_xAxisProps.set( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ), uno::UNO_QUERY );
393 
394     if( m_bAddMissingXAxisForNetCharts && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y && m_aCurrentAxis.nAxisIndex==0 )
395     {
396         try
397         {
398             xDiaProp->setPropertyValue("HasXAxis", uno::makeAny(true) );
399         }
400         catch( beans::UnknownPropertyException & )
401         {
402             SAL_INFO("xmloff.chart", "Couldn't turn on x axis" );
403         }
404     }
405 
406     // set properties
407     if( !m_xAxisProps.is())
408         return;
409 
410     uno::Any aTrueBool( uno::makeAny( true ));
411     uno::Any aFalseBool( uno::makeAny( false ));
412 
413     // #i109879# the line color is black as default, in the model it is a light gray
414     m_xAxisProps->setPropertyValue("LineColor",
415                                  uno::makeAny( COL_BLACK ));
416 
417     m_xAxisProps->setPropertyValue("DisplayLabels", aFalseBool );
418 
419     // Compatibility option: starting from LibreOffice 5.1 the rotated
420     // layout is preferred to staggering for axis labels.
421     // So the import default value for having compatibility with ODF
422     // documents created with earlier LibreOffice versions is `true`.
423     if( GetImport().getGeneratorVersion() != SvXMLImport::ProductVersionUnknown )
424         m_xAxisProps->setPropertyValue("TryStaggeringFirst", aTrueBool );
425 
426     // #88077# AutoOrigin 'on' is default
427     m_xAxisProps->setPropertyValue("AutoOrigin", aTrueBool );
428 
429     if( m_bAxisTypeImported )
430         m_xAxisProps->setPropertyValue("AxisType", uno::makeAny(m_nAxisType) );
431 
432     if( !m_aAutoStyleName.isEmpty())
433     {
434         const SvXMLStylesContext* pStylesCtxt = m_rImportHelper.GetAutoStylesContext();
435         if (pStylesCtxt)
436         {
437             SvXMLStyleContext* pStyle = const_cast<SvXMLStyleContext*>(pStylesCtxt->FindStyleChildContext(SchXMLImportHelper::GetChartFamilyID(), m_aAutoStyleName));
438 
439             if (XMLPropStyleContext * pPropStyleContext = dynamic_cast<XMLPropStyleContext*>(pStyle))
440             {
441                 pPropStyleContext->FillPropertySet(m_xAxisProps);
442 
443                 if( m_bAdaptWrongPercentScaleValues && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y )
444                 {
445                     //set scale data of added x axis back to default
446                     Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(),
447                                         m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) );
448                     if( xAxis.is() )
449                     {
450                         chart2::ScaleData aScaleData( xAxis->getScaleData());
451                         if( lcl_AdaptWrongPercentScaleValues(aScaleData) )
452                             xAxis->setScaleData( aScaleData );
453                     }
454                 }
455 
456                 if( m_bAddMissingXAxisForNetCharts )
457                 {
458                     //copy style from y axis to added x axis:
459 
460                     Reference< chart::XAxisSupplier > xAxisSuppl( xDiaProp, uno::UNO_QUERY );
461                     if( xAxisSuppl.is() )
462                     {
463                         Reference< beans::XPropertySet > xXAxisProp( xAxisSuppl->getAxis(0), uno::UNO_QUERY );
464                         pPropStyleContext->FillPropertySet(xXAxisProp);
465                     }
466 
467                     //set scale data of added x axis back to default
468                     Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(),
469                                         0 /*nDimensionIndex*/, 0 /*nAxisIndex*/ ) );
470                     if( xAxis.is() )
471                     {
472                         chart2::ScaleData aScaleData;
473                         aScaleData.AxisType = chart2::AxisType::CATEGORY;
474                         aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL;
475                         xAxis->setScaleData( aScaleData );
476                     }
477 
478                     //set line style of added x axis to invisible
479                     Reference< beans::XPropertySet > xNewAxisProp( xAxis, uno::UNO_QUERY );
480                     if( xNewAxisProp.is() )
481                     {
482                         xNewAxisProp->setPropertyValue("LineStyle"
483                             , uno::makeAny(drawing::LineStyle_NONE));
484                     }
485                 }
486 
487                 if( m_bAdaptXAxisOrientationForOld2DBarCharts && m_aCurrentAxis.eDimension == SCH_XML_AXIS_X )
488                 {
489                     bool bIs3DChart = false;
490                     if( xDiaProp.is() && ( xDiaProp->getPropertyValue("Dim3D") >>= bIs3DChart )
491                         && !bIs3DChart )
492                     {
493                         Reference< chart2::XChartDocument > xChart2Document( GetImport().GetModel(), uno::UNO_QUERY );
494                         if( xChart2Document.is() )
495                         {
496                             Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xChart2Document->getFirstDiagram(), uno::UNO_QUERY );
497                             if( xCooSysCnt.is() )
498                             {
499                                 uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() );
500                                 if( aCooSysSeq.hasElements() )
501                                 {
502                                     bool bSwapXandYAxis = false;
503                                     Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] );
504                                     Reference< beans::XPropertySet > xCooSysProp( xCooSys, uno::UNO_QUERY );
505                                     if( xCooSysProp.is() && ( xCooSysProp->getPropertyValue("SwapXAndYAxis") >>= bSwapXandYAxis )
506                                         && bSwapXandYAxis )
507                                     {
508                                         Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( 0, m_aCurrentAxis.nAxisIndex );
509                                         if( xAxis.is() )
510                                         {
511                                             chart2::ScaleData aScaleData = xAxis->getScaleData();
512                                             aScaleData.Orientation = chart2::AxisOrientation_REVERSE;
513                                             xAxis->setScaleData( aScaleData );
514                                         }
515                                     }
516                                 }
517                             }
518                         }
519                     }
520                 }
521 
522                 m_rbAxisPositionAttributeImported = m_rbAxisPositionAttributeImported || SchXMLTools::getPropertyFromContext(
523                     u"CrossoverPosition", pPropStyleContext, pStylesCtxt ).hasValue();
524             }
525         }
526     }
527 
528     if (m_aCurrentAxis.eDimension != SCH_XML_AXIS_X)
529         return;
530 
531     Reference<chart2::XAxis> xAxis(lcl_getAxis(GetImport().GetModel(), m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex));
532     if (!xAxis.is())
533         return;
534 
535     chart2::ScaleData aScaleData(xAxis->getScaleData());
536     bool bIs3DChart = false;
537     double fMajorOrigin = -1;
538     OUString sChartType = m_xDiagram->getDiagramType();
539     if ((xDiaProp->getPropertyValue("Dim3D") >>= bIs3DChart) && bIs3DChart
540         && (sChartType == "com.sun.star.chart.BarDiagram" || sChartType == "com.sun.star.chart.StockDiagram"))
541     {
542         aScaleData.ShiftedCategoryPosition = true;
543         xAxis->setScaleData(aScaleData);
544     }
545     else if ((m_xAxisProps->getPropertyValue("MajorOrigin") >>= fMajorOrigin)
546             && (rtl::math::approxEqual(fMajorOrigin, 0.0) || rtl::math::approxEqual(fMajorOrigin, 0.5)))
547     {
548         aScaleData.ShiftedCategoryPosition = rtl::math::approxEqual(fMajorOrigin, 0.5);
549         xAxis->setScaleData(aScaleData);
550     }
551 }
552 
SetAxisTitle()553 void SchXMLAxisContext::SetAxisTitle()
554 {
555     if( m_aCurrentAxis.aTitle.isEmpty() )
556         return;
557 
558     Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) );
559     if( !xAxis.is() )
560         return;
561 
562     Reference< beans::XPropertySet > xTitleProp( xAxis->getAxisTitle() );
563     if( xTitleProp.is() )
564     {
565         try
566         {
567             xTitleProp->setPropertyValue("String", uno::makeAny(m_aCurrentAxis.aTitle) );
568         }
569         catch( beans::UnknownPropertyException & )
570         {
571             SAL_INFO("xmloff.chart", "Property String for Title not available" );
572         }
573     }
574 }
575 
createFastChildContext(sal_Int32 nElement,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)576 css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLAxisContext::createFastChildContext(
577     sal_Int32 nElement,
578     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
579 {
580     switch( nElement )
581     {
582         case XML_ELEMENT(CHART, XML_TITLE):
583         {
584             Reference< drawing::XShape > xTitleShape = getTitleShape();
585             return new SchXMLTitleContext( m_rImportHelper, GetImport(),
586                                                m_aCurrentAxis.aTitle,
587                                                xTitleShape );
588         }
589         break;
590 
591         case XML_ELEMENT(CHART, XML_CATEGORIES):
592             m_aCurrentAxis.bHasCategories = true;
593             return new SchXMLCategoriesContext( GetImport(),
594                                                 m_rCategoriesAddress );
595             break;
596 
597         case  XML_ELEMENT(CHART, XML_DATE_SCALE):
598         case  XML_ELEMENT(CHART_EXT, XML_DATE_SCALE):
599             m_bDateScaleImported = true;
600             return new DateScaleContext( GetImport(), m_xAxisProps );
601 
602         case XML_ELEMENT(CHART, XML_GRID):
603         {
604             bool bIsMajor = true;       // default value for class is "major"
605             OUString sAutoStyleName;
606 
607             for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
608             {
609                 switch (aIter.getToken())
610                 {
611                     case XML_ELEMENT(CHART, XML_CLASS):
612                         if( IsXMLToken( aIter, XML_MINOR ) )
613                             bIsMajor = false;
614                         break;
615                     case XML_ELEMENT(CHART, XML_STYLE_NAME):
616                         sAutoStyleName = aIter.toString();
617                         break;
618                     default:
619                         XMLOFF_WARN_UNKNOWN("xmloff", aIter);
620                 }
621             }
622 
623             CreateGrid( sAutoStyleName, bIsMajor );
624 
625             // don't create a context => use default context. grid elements are empty
626         }
627         break;
628 
629         default:
630             XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
631             break;
632     }
633 
634     return nullptr;
635 }
636 
endFastElement(sal_Int32)637 void SchXMLAxisContext::endFastElement(sal_Int32 )
638 {
639     if( !m_bDateScaleImported && m_nAxisType==chart::ChartAxisType::AUTOMATIC )
640     {
641         Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(), m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) );
642         if( xAxis.is() )
643         {
644             chart2::ScaleData aScaleData( xAxis->getScaleData());
645             aScaleData.AutoDateAxis = false;//different default for older documents
646             xAxis->setScaleData( aScaleData );
647         }
648     }
649 
650     SetAxisTitle();
651 }
652 
653 namespace
654 {
655 
lcl_getAxis(const Reference<chart2::XCoordinateSystem> & rCooSys,sal_Int32 nDimensionIndex,sal_Int32 nAxisIndex)656 Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem >& rCooSys, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
657 {
658     Reference< chart2::XAxis > xAxis;
659     try
660     {
661         xAxis = rCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex );
662     }
663     catch( uno::Exception & )
664     {
665     }
666     return xAxis;
667 }
668 
669 } // anonymous namespace
670 
CorrectAxisPositions(const Reference<chart2::XChartDocument> & xNewDoc,std::u16string_view rChartTypeServiceName,std::u16string_view rODFVersionOfFile,bool bAxisPositionAttributeImported)671 void SchXMLAxisContext::CorrectAxisPositions( const Reference< chart2::XChartDocument >& xNewDoc,
672                           std::u16string_view rChartTypeServiceName,
673                           std::u16string_view rODFVersionOfFile,
674                           bool bAxisPositionAttributeImported )
675 {
676     if( !(rODFVersionOfFile.empty() || rODFVersionOfFile == u"1.0" || rODFVersionOfFile == u"1.1"
677         || ( rODFVersionOfFile == u"1.2" && !bAxisPositionAttributeImported )) )
678         return;
679 
680     try
681     {
682         Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDoc->getFirstDiagram(), uno::UNO_QUERY_THROW );
683         uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems());
684         if( aCooSysSeq.hasElements() )
685         {
686             Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] );
687             if( xCooSys.is() )
688             {
689                 Reference< chart2::XAxis > xMainXAxis = lcl_getAxis( xCooSys, 0, 0 );
690                 Reference< chart2::XAxis > xMainYAxis = lcl_getAxis( xCooSys, 1, 0 );
691                 //Reference< chart2::XAxis > xMajorZAxis = lcl_getAxis( xCooSys, 2, 0 );
692                 Reference< chart2::XAxis > xSecondaryXAxis = lcl_getAxis( xCooSys, 0, 1 );
693                 Reference< chart2::XAxis > xSecondaryYAxis = lcl_getAxis( xCooSys, 1, 1 );
694 
695                 Reference< beans::XPropertySet > xMainXAxisProp( xMainXAxis, uno::UNO_QUERY );
696                 Reference< beans::XPropertySet > xMainYAxisProp( xMainYAxis, uno::UNO_QUERY );
697                 Reference< beans::XPropertySet > xSecondaryXAxisProp( xSecondaryXAxis, uno::UNO_QUERY );
698                 Reference< beans::XPropertySet > xSecondaryYAxisProp( xSecondaryYAxis, uno::UNO_QUERY );
699 
700                 if( xMainXAxisProp.is() && xMainYAxisProp.is() )
701                 {
702                     chart2::ScaleData aMainXScale = xMainXAxis->getScaleData();
703                     if( rChartTypeServiceName == u"com.sun.star.chart2.ScatterChartType" )
704                     {
705                         xMainYAxisProp->setPropertyValue("CrossoverPosition"
706                                 , uno::makeAny( css::chart::ChartAxisPosition_VALUE) );
707                         double fCrossoverValue = 0.0;
708                         aMainXScale.Origin >>= fCrossoverValue;
709                         xMainYAxisProp->setPropertyValue("CrossoverValue"
710                                 , uno::makeAny( fCrossoverValue ) );
711 
712                         if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE )
713                         {
714                             xMainYAxisProp->setPropertyValue("LabelPosition"
715                                 , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_END) );
716                             xMainYAxisProp->setPropertyValue("MarkPosition"
717                                 , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
718                             if( xSecondaryYAxisProp.is() )
719                                 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
720                                 , uno::makeAny( css::chart::ChartAxisPosition_START) );
721                         }
722                         else
723                         {
724                             xMainYAxisProp->setPropertyValue("LabelPosition"
725                                 , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_START) );
726                             xMainYAxisProp->setPropertyValue("MarkPosition"
727                                 , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
728                             if( xSecondaryYAxisProp.is() )
729                                 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
730                                 , uno::makeAny( css::chart::ChartAxisPosition_END) );
731                         }
732                     }
733                     else
734                     {
735                         if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE )
736                         {
737                             xMainYAxisProp->setPropertyValue("CrossoverPosition"
738                                 , uno::makeAny( css::chart::ChartAxisPosition_END) );
739                             if( xSecondaryYAxisProp.is() )
740                                 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
741                                     , uno::makeAny( css::chart::ChartAxisPosition_START) );
742                         }
743                         else
744                         {
745                             xMainYAxisProp->setPropertyValue("CrossoverPosition"
746                                 , uno::makeAny( css::chart::ChartAxisPosition_START) );
747                             if( xSecondaryYAxisProp.is() )
748                                 xSecondaryYAxisProp->setPropertyValue("CrossoverPosition"
749                                     , uno::makeAny( css::chart::ChartAxisPosition_END) );
750                         }
751                     }
752 
753                     chart2::ScaleData aMainYScale = xMainYAxis->getScaleData();
754                     xMainXAxisProp->setPropertyValue("CrossoverPosition"
755                             , uno::makeAny( css::chart::ChartAxisPosition_VALUE) );
756                     double fCrossoverValue = 0.0;
757                     aMainYScale.Origin >>= fCrossoverValue;
758                     xMainXAxisProp->setPropertyValue("CrossoverValue"
759                             , uno::makeAny( fCrossoverValue ) );
760 
761                     if( aMainYScale.Orientation == chart2::AxisOrientation_REVERSE )
762                     {
763                         xMainXAxisProp->setPropertyValue("LabelPosition"
764                             , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_END) );
765                         xMainXAxisProp->setPropertyValue("MarkPosition"
766                             , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
767                         if( xSecondaryXAxisProp.is() )
768                             xSecondaryXAxisProp->setPropertyValue("CrossoverPosition"
769                             , uno::makeAny( css::chart::ChartAxisPosition_START) );
770                     }
771                     else
772                     {
773                         xMainXAxisProp->setPropertyValue("LabelPosition"
774                             , uno::makeAny( css::chart::ChartAxisLabelPosition_OUTSIDE_START) );
775                         xMainXAxisProp->setPropertyValue("MarkPosition"
776                             , uno::makeAny( css::chart::ChartAxisMarkPosition_AT_LABELS) );
777                         if( xSecondaryXAxisProp.is() )
778                             xSecondaryXAxisProp->setPropertyValue("CrossoverPosition"
779                             , uno::makeAny( css::chart::ChartAxisPosition_END) );
780                     }
781                 }
782             }
783         }
784     }
785     catch( uno::Exception & )
786     {
787     }
788 }
789 
SchXMLCategoriesContext(SvXMLImport & rImport,OUString & rAddress)790 SchXMLCategoriesContext::SchXMLCategoriesContext(
791     SvXMLImport& rImport,
792     OUString& rAddress ) :
793         SvXMLImportContext( rImport ),
794         mrAddress( rAddress )
795 {
796 }
797 
startFastElement(sal_Int32,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)798 void SchXMLCategoriesContext::startFastElement( sal_Int32 /*nElement*/,
799     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
800 {
801     for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
802     {
803         if( aIter.getToken() == XML_ELEMENT(TABLE, XML_CELL_RANGE_ADDRESS) )
804             mrAddress = aIter.toString();
805         else
806             XMLOFF_WARN_UNKNOWN("xmloff", aIter);
807     }
808 }
809 
DateScaleContext(SvXMLImport & rImport,const Reference<beans::XPropertySet> & rAxisProps)810 DateScaleContext::DateScaleContext(
811     SvXMLImport& rImport,
812     const Reference< beans::XPropertySet >& rAxisProps ) :
813         SvXMLImportContext( rImport ),
814         m_xAxisProps( rAxisProps )
815 {
816 }
817 
818 namespace
819 {
lcl_getTimeUnit(const sax_fastparser::FastAttributeList::FastAttributeIter & rValue)820 sal_Int32 lcl_getTimeUnit( const sax_fastparser::FastAttributeList::FastAttributeIter& rValue )
821 {
822     sal_Int32 nTimeUnit = css::chart::TimeUnit::DAY;
823     if( IsXMLToken( rValue, XML_DAYS ) )
824         nTimeUnit = css::chart::TimeUnit::DAY;
825     else if( IsXMLToken( rValue, XML_MONTHS ) )
826         nTimeUnit = css::chart::TimeUnit::MONTH;
827     else if( IsXMLToken( rValue, XML_YEARS ) )
828         nTimeUnit = css::chart::TimeUnit::YEAR;
829     return nTimeUnit;
830 }
831 
832 }
833 
startFastElement(sal_Int32,const css::uno::Reference<css::xml::sax::XFastAttributeList> & xAttrList)834 void DateScaleContext::startFastElement( sal_Int32 /*nElement*/,
835     const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
836 {
837     if( !m_xAxisProps.is() )
838         return;
839 
840     // parse attributes
841     bool bSetNewIncrement=false;
842     chart::TimeIncrement aIncrement;
843     m_xAxisProps->getPropertyValue("TimeIncrement") >>= aIncrement;
844 
845     for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
846     {
847         switch( aIter.getToken() )
848         {
849             case  XML_ELEMENT(CHART, XML_BASE_TIME_UNIT):
850                 {
851                     aIncrement.TimeResolution <<= lcl_getTimeUnit(aIter);
852                     bSetNewIncrement = true;
853                 }
854                 break;
855             case XML_ELEMENT(CHART, XML_MAJOR_INTERVAL_VALUE):
856                 {
857                     chart::TimeInterval aInterval(1,0);
858                     aIncrement.MajorTimeInterval >>= aInterval;
859                     ::sax::Converter::convertNumber( aInterval.Number, aIter.toView() );
860                     aIncrement.MajorTimeInterval <<= aInterval;
861                     bSetNewIncrement = true;
862                 }
863                 break;
864             case  XML_ELEMENT(CHART, XML_MAJOR_INTERVAL_UNIT):
865                 {
866                     chart::TimeInterval aInterval(1,0);
867                     aIncrement.MajorTimeInterval >>= aInterval;
868                     aInterval.TimeUnit = lcl_getTimeUnit(aIter);
869                     aIncrement.MajorTimeInterval <<= aInterval;
870                     bSetNewIncrement = true;
871                 }
872                 break;
873             case XML_ELEMENT(CHART, XML_MINOR_INTERVAL_VALUE):
874                 {
875                     chart::TimeInterval aInterval(1,0);
876                     aIncrement.MinorTimeInterval >>= aInterval;
877                     ::sax::Converter::convertNumber( aInterval.Number, aIter.toView() );
878                     aIncrement.MinorTimeInterval <<= aInterval;
879                     bSetNewIncrement = true;
880                 }
881                 break;
882             case  XML_ELEMENT(CHART, XML_MINOR_INTERVAL_UNIT):
883                 {
884                     chart::TimeInterval aInterval(1,0);
885                     aIncrement.MinorTimeInterval >>= aInterval;
886                     aInterval.TimeUnit = lcl_getTimeUnit(aIter);
887                     aIncrement.MinorTimeInterval <<= aInterval;
888                     bSetNewIncrement = true;
889                 }
890                 break;
891             default:
892                 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
893         }
894     }
895 
896     if( bSetNewIncrement )
897         m_xAxisProps->setPropertyValue("TimeIncrement", uno::makeAny( aIncrement ) );
898 }
899 
900 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
901