diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /xmloff/source/chart | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
56 files changed, 16848 insertions, 0 deletions
diff --git a/xmloff/source/chart/ColorPropertySet.cxx b/xmloff/source/chart/ColorPropertySet.cxx new file mode 100644 index 000000000..76050be40 --- /dev/null +++ b/xmloff/source/chart/ColorPropertySet.cxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ColorPropertySet.hxx" + +#include <cppuhelper/implbase.hxx> + +#include <osl/diagnose.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ +class lcl_ColorPropertySetInfo : public ::cppu::WeakImplHelper< + XPropertySetInfo > +{ +public: + explicit lcl_ColorPropertySetInfo(); + +protected: + // ____ XPropertySetInfo ____ + virtual Sequence< Property > SAL_CALL getProperties() override; + virtual Property SAL_CALL getPropertyByName( const OUString& aName ) override; + virtual sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override; + +private: + static constexpr OUStringLiteral g_aColorPropName = u"FillColor"; + Property m_aColorProp; +}; + +lcl_ColorPropertySetInfo::lcl_ColorPropertySetInfo() : + m_aColorProp( g_aColorPropName, -1, + cppu::UnoType<sal_Int32>::get(), 0) +{} + +Sequence< Property > SAL_CALL lcl_ColorPropertySetInfo::getProperties() +{ + + return Sequence< Property >( & m_aColorProp, 1 ); +} + +Property SAL_CALL lcl_ColorPropertySetInfo::getPropertyByName( const OUString& aName ) +{ + if( aName == g_aColorPropName ) + return m_aColorProp; + throw UnknownPropertyException( g_aColorPropName, static_cast< uno::XWeak * >( this )); +} + +sal_Bool SAL_CALL lcl_ColorPropertySetInfo::hasPropertyByName( const OUString& Name ) +{ + return Name == g_aColorPropName; +} + +} // anonymous namespace + +namespace xmloff::chart +{ + +ColorPropertySet::ColorPropertySet( ::Color nColor ) : + m_nColor( nColor ), + m_nDefaultColor( 0x0099ccff ) // blue 8 +{} + +ColorPropertySet::~ColorPropertySet() +{} + +// ____ XPropertySet ____ + +Reference< XPropertySetInfo > SAL_CALL ColorPropertySet::getPropertySetInfo() +{ + if( ! m_xInfo.is()) + m_xInfo.set( new lcl_ColorPropertySetInfo ); + + return m_xInfo; +} + +void SAL_CALL ColorPropertySet::setPropertyValue( const OUString& /* aPropertyName */, const uno::Any& aValue ) +{ + aValue >>= m_nColor; +} + +uno::Any SAL_CALL ColorPropertySet::getPropertyValue( const OUString& /* PropertyName */ ) +{ + return uno::Any( m_nColor ); +} + +void SAL_CALL ColorPropertySet::addPropertyChangeListener( const OUString& /* aPropertyName */, const Reference< XPropertyChangeListener >& /* xListener */ ) +{ + OSL_FAIL( "Not Implemented" ); +} + +void SAL_CALL ColorPropertySet::removePropertyChangeListener( const OUString& /* aPropertyName */, const Reference< XPropertyChangeListener >& /* aListener */ ) +{ + OSL_FAIL( "Not Implemented" ); +} + +void SAL_CALL ColorPropertySet::addVetoableChangeListener( const OUString& /* PropertyName */, const Reference< XVetoableChangeListener >& /* aListener */ ) +{ + OSL_FAIL( "Not Implemented" ); +} + +void SAL_CALL ColorPropertySet::removeVetoableChangeListener( const OUString& /* PropertyName */, const Reference< XVetoableChangeListener >& /* aListener */ ) +{ + OSL_FAIL( "Not Implemented" ); +} + +// ____ XPropertyState ____ + +PropertyState SAL_CALL ColorPropertySet::getPropertyState( const OUString& /* PropertyName */ ) +{ + return PropertyState_DIRECT_VALUE; +} + +Sequence< PropertyState > SAL_CALL ColorPropertySet::getPropertyStates( const Sequence< OUString >& /* aPropertyName */ ) +{ + PropertyState aState = PropertyState_DIRECT_VALUE; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + return Sequence<PropertyState>(&aState, 1); +} + +void SAL_CALL ColorPropertySet::setPropertyToDefault( const OUString& PropertyName ) +{ + if( PropertyName == g_aColorPropName ) + m_nColor = m_nDefaultColor; +} + +uno::Any SAL_CALL ColorPropertySet::getPropertyDefault( const OUString& aPropertyName ) +{ + if( aPropertyName == g_aColorPropName ) + return uno::Any( m_nDefaultColor ); + return uno::Any(); +} + +} // namespace xmloff::chart + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/ColorPropertySet.hxx b/xmloff/source/chart/ColorPropertySet.hxx new file mode 100644 index 000000000..d5b42e39d --- /dev/null +++ b/xmloff/source/chart/ColorPropertySet.hxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#ifndef XMLOFF_COLORPROPERTYSET_HXX +#define XMLOFF_COLORPROPERTYSET_HXX + +#include <cppuhelper/implbase.hxx> +#include <tools/color.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> + +namespace xmloff::chart +{ + +class ColorPropertySet : public ::cppu::WeakImplHelper< + css::beans::XPropertySet, + css::beans::XPropertyState > +{ +public: + explicit ColorPropertySet( ::Color nColor ); + virtual ~ColorPropertySet() override; + +protected: + // ____ XPropertySet ____ + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue( + const OUString& aPropertyName, + const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( + const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( + const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( + const OUString& aPropertyName, + const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( + const OUString& PropertyName, + const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( + const OUString& PropertyName, + const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // ____ XPropertyState ____ + virtual css::beans::PropertyState SAL_CALL getPropertyState( + const OUString& PropertyName ) override; + virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL getPropertyStates( + const css::uno::Sequence< OUString >& aPropertyName ) override; + virtual void SAL_CALL setPropertyToDefault( + const OUString& PropertyName ) override; + virtual css::uno::Any SAL_CALL getPropertyDefault( + const OUString& aPropertyName ) override; + +private: + css::uno::Reference< css::beans::XPropertySetInfo > m_xInfo; + static constexpr OUStringLiteral g_aColorPropName = u"FillColor"; + Color m_nColor; + Color m_nDefaultColor; +}; + +} // namespace xmloff::chart + +// XMLOFF_COLORPROPERTYSET_HXX +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/MultiPropertySetHandler.hxx b/xmloff/source/chart/MultiPropertySetHandler.hxx new file mode 100644 index 000000000..d47b942f0 --- /dev/null +++ b/xmloff/source/chart/MultiPropertySetHandler.hxx @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <map> +#include <memory> +#include <string_view> + +#include <rtl/ustring.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XMultiPropertySet.hpp> + +/** @descr MultiPropertySetHandler handles the two slightly different + interfaces XPropertySet and XMultiPropertySet for accessing + properties of an object. + + It uses the classes PropertyWrapperBase and the template + PropertyWrapper for a type safe access to single properties. + + The function class OUStringComparison is used by a STL map to + sort the properties by names. +*/ + +/** @descr Base class for the templated property wrappers. + Having a common base class allows to set a variable to the + property's value without explicit knowledge of its type. +*/ +class PropertyWrapperBase +{ +public: + /** @descr Create a class instance and store the given name. + @param rName The name of the property. + */ + explicit PropertyWrapperBase (const OUString & rName) + : msName (rName) + {} + virtual ~PropertyWrapperBase() + {} + + /** @descr Abstract interface of a method for setting a variables + value to that of the property. + */ + virtual void SetValue (const css::uno::Any & rValue) = 0; + + const OUString msName; +}; + +/** @descr For every property type there will be one instantiation of this + template class with its own and type specific version of SetValue. +*/ +template<class T> class PropertyWrapper : public PropertyWrapperBase +{ +public: + /** @descr Create a wrapper for a property of type T. + */ + PropertyWrapper (const OUString & rName, T & rValue) + : PropertyWrapperBase (rName), + mrValue (rValue) + {} + + /** descr Set the given value inside an Any to the variable referenced + by the data member. + */ + virtual void SetValue (const css::uno::Any & rValue) override + { + rValue >>= mrValue; + } + +private: + /// Reference to a variable. Its value can be modified by a call to SetValue. + T & mrValue; +}; + +/** @descr Function object for comparing two OUStrings. +*/ +class OUStringComparison +{ +public: + /// Compare two strings. Returns true if the first is before the second. + bool operator() (std::u16string_view a, std::u16string_view b) const + { + return (a.compare (b) < 0); + } +}; + +/** @descr This class lets you get the values from an object that either + supports the interface XPropertySet or XMultiPropertySet. If it + supports both interfaces then XMultiPropertySet is preferred. + + Using it works in three steps. + 1. Create an instance and pass a reference to the object from which to + get the property values. + 2. Make all properties whose values you want to get known to the object + by using the Add method. This creates instances of a template class + that stores the properties name and a reference to the variable in + which to store its value. + 3. Finally call GetProperties to store the properties values into the + variables specified in step 2. This uses either the XPropertySet or + (preferred) the XMultiPropertySet interface. +*/ +class MultiPropertySetHandler +{ +public: + /** @descr Create a handler of the property set of the given + object. + @param xObject A reference to any of the object's interfaces. + not necessarily XPropertySet or XMultiPropertySet. It + is casted later to one of the two of them. + */ + explicit MultiPropertySetHandler (css::uno::Reference< + css::uno::XInterface> const & xObject); + /** @descr Add a property to handle. The type given implicitly by the + reference to a variable is used to create an instance of + the PropertyWrapper template class. + @param sName Name of the property. + @param rValue Reference to a variable whose value is set by the + call to GetProperties to the property's value. + */ + template<class T> void Add (const OUString & sName, T& rValue) + { + aPropertyList[sName] = std::make_unique<PropertyWrapper<T>>(sName, rValue); + } + + /** @descr Try to get the values for all properties added with the Add + method. If possible it uses the XMultiPropertySet. If that fails + (i.e. for an UnknownPropertyExcption) or if the interface is not + supported it uses the XPropertySet interface. + @return If none of the two interfaces is supported or using them both + fails then sal_False is returned. Else True is returned. + */ + inline bool GetProperties(); + +private: + /** @descr Try to use the XMultiPropertySet interface to get the property + values. + @param rNameList A precomputed and sorted sequence of OUStrings + containing the properties names. + @return True if values could be derived. + */ + inline bool MultiGet (const css::uno::Sequence< + OUString> & rNameList); + + /** @descr Try to use the XPropertySet interface to get the property + values. + @param rNameList A precomputed and sorted sequence of OUStrings + containing the properties names. + @return True if values could be derived. + */ + inline bool SingleGet (const css::uno::Sequence< + OUString> & rNameList); + + /** @descr STL map that maps from property names to polymorphic instances of + PropertyWrapper. It uses OUStringComparison for sorting + the property names. + */ + ::std::map< OUString, std::unique_ptr<PropertyWrapperBase>, OUStringComparison> aPropertyList; + + /// The object from which to get the property values. + css::uno::Reference< css::uno::XInterface> mxObject; +}; + +MultiPropertySetHandler::MultiPropertySetHandler (css::uno::Reference< + css::uno::XInterface> const & xObject) + : mxObject (xObject) +{ +} + +bool MultiPropertySetHandler::GetProperties() +{ + css::uno::Sequence< OUString> aNameList (aPropertyList.size()); + auto aNameListRange = asNonConstRange(aNameList); + int i = 0; + for (const auto& rProperty : aPropertyList) + aNameListRange[i++] = rProperty.second->msName; + if ( ! MultiGet(aNameList)) + if ( ! SingleGet(aNameList)) + return false; + return true; +} + +bool MultiPropertySetHandler::MultiGet (const css::uno::Sequence< + OUString> & rNameList) +{ + css::uno::Reference< css::beans::XMultiPropertySet> xMultiSet ( + mxObject, css::uno::UNO_QUERY); + if (xMultiSet.is()) + try + { + int i = 0; + css::uno::Sequence< css::uno::Any> aValueList = + xMultiSet->getPropertyValues (rNameList); + for (auto& rProperty : aPropertyList) + rProperty.second->SetValue (aValueList[i++]); + } + catch (const css::beans::UnknownPropertyException&) + { + return false; + } + else + return false; + + return true; +} + +bool MultiPropertySetHandler::SingleGet (const css::uno::Sequence< + OUString> & rNameList) +{ + css::uno::Reference< css::beans::XPropertySet> xSingleSet ( + mxObject, css::uno::UNO_QUERY); + if (xSingleSet.is()) + try + { + int i = 0; + for (auto& rProperty : aPropertyList) + rProperty.second->SetValue (xSingleSet->getPropertyValue (rNameList[i++])); + } + catch (const css::beans::UnknownPropertyException&) + { + return false; + } + else + return false; + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/PropertyMap.hxx b/xmloff/source/chart/PropertyMap.hxx new file mode 100644 index 000000000..f9a3dc4e3 --- /dev/null +++ b/xmloff/source/chart/PropertyMap.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/maptype.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltypes.hxx> +#include <xmloff/contextid.hxx> +#include <xmloff/xmlement.hxx> +#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp> +#include <com/sun/star/chart/ChartAxisLabelPosition.hpp> +#include <com/sun/star/chart/ChartAxisMarkPosition.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart/ChartSolidType.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart/MissingValueTreatment.hpp> + +// custom types +#define XML_SCH_TYPE_AXIS_ARRANGEMENT ( XML_SCH_TYPES_START + 0 ) +#define XML_SCH_TYPE_ERROR_BAR_STYLE ( XML_SCH_TYPES_START + 1 ) +// free +#define XML_SCH_TYPE_SOLID_TYPE ( XML_SCH_TYPES_START + 3 ) +#define XML_SCH_TYPE_ERROR_INDICATOR_UPPER ( XML_SCH_TYPES_START + 4 ) +#define XML_SCH_TYPE_ERROR_INDICATOR_LOWER ( XML_SCH_TYPES_START + 5 ) +#define XML_SCH_TYPE_DATAROWSOURCE ( XML_SCH_TYPES_START + 6 ) +#define XML_SCH_TYPE_TEXT_ORIENTATION ( XML_SCH_TYPES_START + 7 ) +#define XML_SCH_TYPE_INTERPOLATION ( XML_SCH_TYPES_START + 8 ) +#define XML_SCH_TYPE_SYMBOL_TYPE ( XML_SCH_TYPES_START + 9 ) +#define XML_SCH_TYPE_NAMED_SYMBOL ( XML_SCH_TYPES_START + 10 ) +#define XML_SCH_TYPE_LABEL_PLACEMENT_TYPE ( XML_SCH_TYPES_START + 11 ) +#define XML_SCH_TYPE_MISSING_VALUE_TREATMENT ( XML_SCH_TYPES_START + 12 ) +#define XML_SCH_TYPE_AXIS_POSITION ( XML_SCH_TYPES_START + 13 ) +#define XML_SCH_TYPE_AXIS_POSITION_VALUE ( XML_SCH_TYPES_START + 14 ) +#define XML_SCH_TYPE_AXIS_LABEL_POSITION ( XML_SCH_TYPES_START + 15 ) +#define XML_SCH_TYPE_TICK_MARK_POSITION ( XML_SCH_TYPES_START + 16 ) +#define XML_SCH_TYPE_LABEL_BORDER_STYLE ( XML_SCH_TYPES_START + 17 ) +#define XML_SCH_TYPE_LABEL_BORDER_OPACITY ( XML_SCH_TYPES_START + 18 ) +#define XML_SCH_TYPE_LABEL_FILL_STYLE ( XML_SCH_TYPES_START + 19 ) + +// context ids +#define XML_SCH_CONTEXT_USER_SYMBOL ( XML_SCH_CTF_START + 0 ) +#define XML_SCH_CONTEXT_MIN ( XML_SCH_CTF_START + 1 ) +#define XML_SCH_CONTEXT_MAX ( XML_SCH_CTF_START + 2 ) +#define XML_SCH_CONTEXT_STEP_MAIN ( XML_SCH_CTF_START + 3 ) +#define XML_SCH_CONTEXT_STEP_HELP_COUNT ( XML_SCH_CTF_START + 4 ) +#define XML_SCH_CONTEXT_ORIGIN ( XML_SCH_CTF_START + 5 ) +#define XML_SCH_CONTEXT_LOGARITHMIC ( XML_SCH_CTF_START + 6 ) +#define XML_SCH_CONTEXT_STOCK_WITH_VOLUME ( XML_SCH_CTF_START + 7 ) +#define XML_SCH_CONTEXT_LINES_USED ( XML_SCH_CTF_START + 8 ) + +#define XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_INNER ( XML_SCH_CTF_START + 10 ) +#define XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_OUTER ( XML_SCH_CTF_START + 11 ) +#define XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_INNER ( XML_SCH_CTF_START + 12 ) +#define XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_OUTER ( XML_SCH_CTF_START + 13 ) +#define XML_SCH_CONTEXT_SPECIAL_TEXT_ROTATION ( XML_SCH_CTF_START + 14 ) +#define XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_NUMBER ( XML_SCH_CTF_START + 15 ) +#define XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_TEXT ( XML_SCH_CTF_START + 16 ) +#define XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SYMBOL ( XML_SCH_CTF_START + 17 ) +#define XML_SCH_CONTEXT_SPECIAL_NUMBER_FORMAT ( XML_SCH_CTF_START + 18 ) +#define XML_SCH_CONTEXT_SPECIAL_DATA_ROW_SOURCE ( XML_SCH_CTF_START + 19 ) +#define XML_SCH_CONTEXT_SPECIAL_SYMBOL_WIDTH ( XML_SCH_CTF_START + 20 ) +#define XML_SCH_CONTEXT_SPECIAL_SYMBOL_HEIGHT ( XML_SCH_CTF_START + 21 ) +#define XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE_NAME ( XML_SCH_CTF_START + 22 ) +#define XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE ( XML_SCH_CTF_START + 23 ) +#define XML_SCH_CONTEXT_SPECIAL_LABEL_SEPARATOR ( XML_SCH_CTF_START + 24 ) +#define XML_SCH_CONTEXT_SPECIAL_ERRORBAR_RANGE ( XML_SCH_CTF_START + 25 ) +#define XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE ( XML_SCH_CTF_START + 26 ) +#define XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SERIES ( XML_SCH_CTF_START + 27 ) +#define XML_SCH_CONTEXT_SPECIAL_MOVING_AVERAGE_TYPE ( XML_SCH_CTF_START + 28 ) + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/PropertyMaps.cxx b/xmloff/source/chart/PropertyMaps.cxx new file mode 100644 index 000000000..e40ac207a --- /dev/null +++ b/xmloff/source/chart/PropertyMaps.cxx @@ -0,0 +1,1040 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PropertyMap.hxx" + +#include <sax/tools/converter.hxx> + +#include "SchXMLTools.hxx" +#include <XMLChartPropertySetMapper.hxx> +#include "XMLErrorIndicatorPropertyHdl.hxx" +#include "XMLErrorBarStylePropertyHdl.hxx" +#include "XMLTextOrientationHdl.hxx" +#include "XMLSymbolTypePropertyHdl.hxx" +#include "XMLAxisPositionPropertyHdl.hxx" +#include <propimp0.hxx> + +#include <xmloff/EnumPropertyHdl.hxx> +#include <xmloff/attrlist.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/shapeimport.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/prhdlfac.hxx> + +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/chart/ChartAxisMarks.hpp> +#include <com/sun/star/chart/ChartDataCaption.hpp> +#include <com/sun/star/chart2/MovingAverageType.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> + +#include <rtl/ustrbuf.hxx> + +#define SCH_XML_SETFLAG( status, flag ) (status)|= (flag) +#define SCH_XML_UNSETFLAG( status, flag ) (status) = ((status) | (flag)) - (flag) + +using namespace com::sun::star; +using namespace ::xmloff::token; +using namespace css::chart2; + + +#define MAP_FULL( ApiName, NameSpace, XMLTokenName, XMLType, ContextId, EarliestODFVersionForExport ) { ApiName, XML_NAMESPACE_##NameSpace, xmloff::token::XMLTokenName, XMLType|XML_TYPE_PROP_CHART, ContextId, EarliestODFVersionForExport, false } +#define MAP_ENTRY( a, ns, nm, t ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART, 0, SvtSaveOptions::ODFSVER_010, false } +#define MAP_ENTRY_ODF12( a, ns, nm, t ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART, 0, SvtSaveOptions::ODFSVER_012, false } +#define MAP_ENTRY_ODF13( a, ns, nm, t ) { a, ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART, 0, SvtSaveOptions::ODFSVER_013, false } +#define MAP_ENTRY_ODF_EXT( a, ns, nm, t ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART, 0, SvtSaveOptions::ODFSVER_FUTURE_EXTENDED, false } +#define MAP_ENTRY_ODF_EXT_IMPORT( a, ns, nm, t ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART, 0, SvtSaveOptions::ODFSVER_FUTURE_EXTENDED, true } +#define MAP_CONTEXT( a, ns, nm, t, c ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART, c, SvtSaveOptions::ODFSVER_010, false } +#define MAP_SPECIAL( a, ns, nm, t, c ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART | MID_FLAG_SPECIAL_ITEM, c, SvtSaveOptions::ODFSVER_010, false } +#define MAP_SPECIAL_ODF12( a, ns, nm, t, c ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART | MID_FLAG_SPECIAL_ITEM, c, SvtSaveOptions::ODFSVER_012, false } +#define MAP_SPECIAL_ODF13( a, ns, nm, t, c ) { a, XML_NAMESPACE_##ns, xmloff::token::nm, t|XML_TYPE_PROP_CHART | MID_FLAG_SPECIAL_ITEM, c, SvtSaveOptions::ODFSVER_013, false } +#define MAP_ENTRY_END { nullptr,0,xmloff::token::XML_TOKEN_INVALID,0,0,SvtSaveOptions::ODFSVER_010, false } + +// PropertyMap for Chart properties drawing- and +// textproperties are added later using the chaining +// mechanism + +const XMLPropertyMapEntry aXMLChartPropMap[] = +{ + // chart subtypes + MAP_ENTRY( "UpDown", CHART, XML_JAPANESE_CANDLE_STICK, XML_TYPE_BOOL ), // formerly XML_STOCK_UPDOWN_BARS + MAP_CONTEXT( "Volume", CHART, XML_STOCK_WITH_VOLUME, XML_TYPE_BOOL, XML_SCH_CONTEXT_STOCK_WITH_VOLUME ), + MAP_ENTRY( "Dim3D", CHART, XML_THREE_DIMENSIONAL, XML_TYPE_BOOL ), + MAP_ENTRY( "Deep", CHART, XML_DEEP, XML_TYPE_BOOL ), + MAP_ENTRY( "Lines", CHART, XML_LINES, XML_TYPE_BOOL ), + MAP_ENTRY( "Percent", CHART, XML_PERCENTAGE, XML_TYPE_BOOL ), + MAP_ENTRY( "SolidType", CHART, XML_SOLID_TYPE, XML_SCH_TYPE_SOLID_TYPE ), + // ODF 1.3 OFFICE-3662 added values + MAP_ENTRY( "SplineType", CHART, XML_INTERPOLATION, XML_SCH_TYPE_INTERPOLATION ), + MAP_ENTRY( "Stacked", CHART, XML_STACKED, XML_TYPE_BOOL ), + // type: "none", "automatic", "named-symbol" or "image" + MAP_ENTRY( "SymbolType", CHART, XML_SYMBOL_TYPE, XML_SCH_TYPE_SYMBOL_TYPE | MID_FLAG_MULTI_PROPERTY ), + // if type=="named-symbol" => name of symbol (square, diamond, ...) + MAP_ENTRY( "SymbolType", CHART, XML_SYMBOL_NAME, XML_SCH_TYPE_NAMED_SYMBOL | MID_FLAG_MULTI_PROPERTY ), + // if type=="image" => an xlink:href element with a linked (package) URI + MAP_SPECIAL( "SymbolBitmap", CHART, XML_SYMBOL_IMAGE, XML_TYPE_STRING | MID_FLAG_ELEMENT_ITEM, XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE ), + MAP_SPECIAL( "SymbolSize", CHART, XML_SYMBOL_WIDTH, XML_TYPE_MEASURE | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_SYMBOL_WIDTH ), + MAP_SPECIAL( "SymbolSize", CHART, XML_SYMBOL_HEIGHT, XML_TYPE_MEASURE | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_SYMBOL_HEIGHT ), + MAP_ENTRY( "Vertical", CHART, XML_VERTICAL, XML_TYPE_BOOL ), + // #i32368# property should no longer be used as XML-property (in OASIS + // format), but is still ex-/imported for compatibility with the OOo file format + MAP_CONTEXT( "NumberOfLines", CHART, XML_LINES_USED, XML_TYPE_NUMBER, XML_SCH_CONTEXT_LINES_USED ), + MAP_ENTRY( "StackedBarsConnected", CHART, XML_CONNECT_BARS, XML_TYPE_BOOL ), + + MAP_ENTRY_ODF12( "GroupBarsPerAxis", CHART, XML_GROUP_BARS_PER_AXIS, XML_TYPE_BOOL ), + MAP_ENTRY_ODF12( "IncludeHiddenCells", CHART, XML_INCLUDE_HIDDEN_CELLS, XML_TYPE_BOOL ), + MAP_ENTRY_ODF12( "AutomaticPosition", CHART, XML_AUTOMATIC_POSITION, XML_TYPE_BOOL ), + MAP_ENTRY_ODF12( "AutomaticSize", CHART, XML_AUTOMATIC_SIZE, XML_TYPE_BOOL ), + MAP_ENTRY_ODF12( "StartingAngle", CHART, XML_ANGLE_OFFSET, XML_TYPE_NUMBER ), + MAP_ENTRY_ODF12( "MissingValueTreatment", CHART, XML_TREAT_EMPTY_CELLS, XML_SCH_TYPE_MISSING_VALUE_TREATMENT ), + // #72304 Chart data table flags + MAP_ENTRY_ODF_EXT( "DataTableHBorder", LO_EXT, XML_DATA_TABLE_SHOW_HORZ_BORDER, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT( "DataTableVBorder", LO_EXT, XML_DATA_TABLE_SHOW_VERT_BORDER, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT( "DataTableOutline", LO_EXT, XML_DATA_TABLE_SHOW_OUTLINE, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT_IMPORT( "DataTableHBorder", CHART, XML_DATA_TABLE_SHOW_HORZ_BORDER, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT_IMPORT( "DataTableVBorder", CHART, XML_DATA_TABLE_SHOW_VERT_BORDER, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT_IMPORT( "DataTableOutline", CHART, XML_DATA_TABLE_SHOW_OUTLINE, XML_TYPE_BOOL ), + // Chart display units flags + MAP_ENTRY_ODF_EXT( "DisplayUnits", LO_EXT, XML_CHART_DUNITS_DISPLAYUNITS, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT( "BuiltInUnit", LO_EXT, XML_CHART_DUNITS_BUILTINUNIT, XML_TYPE_STRING ), + MAP_ENTRY_ODF_EXT( "ExternalData", LO_EXT, XML_EXTERNALDATA, XML_TYPE_STRING), + + MAP_ENTRY_ODF_EXT( "LabelBorderColor", LO_EXT, XML_LABEL_STROKE_COLOR, XML_TYPE_COLOR ), + MAP_ENTRY_ODF_EXT( "LabelBorderStyle", LO_EXT, XML_LABEL_STROKE, XML_SCH_TYPE_LABEL_BORDER_STYLE ), + MAP_ENTRY_ODF_EXT( "LabelBorderTransparency", LO_EXT, XML_LABEL_STROKE_OPACITY, XML_SCH_TYPE_LABEL_BORDER_OPACITY ), + MAP_ENTRY_ODF_EXT( "LabelBorderWidth", LO_EXT, XML_LABEL_STROKE_WIDTH, XML_TYPE_MEASURE ), + + MAP_ENTRY_ODF_EXT( "LabelFillColor", LO_EXT, XML_LABEL_FILL_COLOR, XML_TYPE_COLOR ), + MAP_ENTRY_ODF_EXT( "LabelFillStyle", LO_EXT, XML_LABEL_FILL, XML_SCH_TYPE_LABEL_FILL_STYLE ), + MAP_ENTRY_ODF_EXT( "LabelFillBackground", LO_EXT, XML_FILL_HATCH_SOLID, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT( "LabelFillHatchName", LO_EXT, XML_FILL_HATCH_NAME, XML_TYPE_STYLENAME), + + MAP_ENTRY( "ScaleText", CHART, XML_SCALE_TEXT, XML_TYPE_BOOL ), + + // spline settings + MAP_ENTRY( "SplineOrder", CHART, XML_SPLINE_ORDER, XML_TYPE_NUMBER ), + MAP_ENTRY( "SplineResolution", CHART, XML_SPLINE_RESOLUTION, XML_TYPE_NUMBER ), + + // plot-area properties + MAP_ENTRY( "DataRowSource", CHART, XML_SERIES_SOURCE, XML_SCH_TYPE_DATAROWSOURCE ), + MAP_ENTRY_ODF12( "SortByXValues", CHART, XML_SORT_BY_X_VALUES, XML_TYPE_BOOL ), + MAP_ENTRY_ODF12( "RightAngledAxes", CHART, XML_RIGHT_ANGLED_AXES, XML_TYPE_BOOL ), + + // axis properties + MAP_ENTRY( "DisplayLabels", CHART, XML_DISPLAY_LABEL, XML_TYPE_BOOL ), + MAP_SPECIAL( "Marks", CHART, XML_TICK_MARKS_MAJOR_INNER, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_INNER ), // convert one constant + MAP_SPECIAL( "Marks", CHART, XML_TICK_MARKS_MAJOR_OUTER, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_OUTER ), // to two bools + MAP_SPECIAL( "HelpMarks", CHART, XML_TICK_MARKS_MINOR_INNER, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_INNER ), // see above + MAP_SPECIAL( "HelpMarks", CHART, XML_TICK_MARKS_MINOR_OUTER, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_OUTER ), + MAP_CONTEXT( "Logarithmic", CHART, XML_LOGARITHMIC, XML_TYPE_BOOL, XML_SCH_CONTEXT_LOGARITHMIC ), + MAP_CONTEXT( "Min", CHART, XML_MINIMUM, XML_TYPE_DOUBLE, XML_SCH_CONTEXT_MIN ), + MAP_CONTEXT( "Max", CHART, XML_MAXIMUM, XML_TYPE_DOUBLE, XML_SCH_CONTEXT_MAX ), + MAP_CONTEXT( "Origin", CHART, XML_ORIGIN, XML_TYPE_DOUBLE, XML_SCH_CONTEXT_ORIGIN ), + MAP_CONTEXT( "StepMain", CHART, XML_INTERVAL_MAJOR, XML_TYPE_DOUBLE, XML_SCH_CONTEXT_STEP_MAIN ), + MAP_CONTEXT( "StepHelpCount", CHART, XML_INTERVAL_MINOR_DIVISOR, XML_TYPE_NUMBER, XML_SCH_CONTEXT_STEP_HELP_COUNT ), + MAP_ENTRY( "GapWidth", CHART, XML_GAP_WIDTH, XML_TYPE_NUMBER ), + MAP_ENTRY( "Overlap", CHART, XML_OVERLAP, XML_TYPE_NUMBER ), + MAP_ENTRY( "TextCanOverlap", CHART, XML_TEXT_OVERLAP, XML_TYPE_BOOL ), + MAP_ENTRY_ODF12( "ReverseDirection", CHART, XML_REVERSE_DIRECTION, XML_TYPE_BOOL ), + MAP_ENTRY( "TextBreak", TEXT, XML_LINE_BREAK, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT( "TryStaggeringFirst", LO_EXT, XML_TRY_STAGGERING_FIRST, XML_TYPE_BOOL ), + MAP_ENTRY( "ArrangeOrder", CHART, XML_LABEL_ARRANGEMENT, XML_SCH_TYPE_AXIS_ARRANGEMENT ), + MAP_SPECIAL( "NumberFormat", STYLE, XML_DATA_STYLE_NAME, XML_TYPE_NUMBER, XML_SCH_CONTEXT_SPECIAL_NUMBER_FORMAT ), + MAP_ENTRY( "LinkNumberFormatToSource", CHART, XML_LINK_DATA_STYLE_TO_SOURCE, XML_TYPE_BOOL ), + MAP_ENTRY( "Visible", CHART, XML_VISIBLE, XML_TYPE_BOOL ), + MAP_ENTRY_ODF_EXT( "MajorOrigin", LO_EXT, XML_MAJOR_ORIGIN, XML_TYPE_DOUBLE ), + + MAP_FULL( "CrossoverPosition", CHART, XML_AXIS_POSITION, XML_SCH_TYPE_AXIS_POSITION|MID_FLAG_MERGE_ATTRIBUTE|MID_FLAG_MULTI_PROPERTY, 0, SvtSaveOptions::ODFSVER_012 ), + MAP_FULL( "CrossoverValue", CHART, XML_AXIS_POSITION, XML_SCH_TYPE_AXIS_POSITION_VALUE|MID_FLAG_MERGE_ATTRIBUTE|MID_FLAG_MULTI_PROPERTY, 0, SvtSaveOptions::ODFSVER_012 ), + MAP_FULL( "LabelPosition", CHART, XML_AXIS_LABEL_POSITION, XML_SCH_TYPE_AXIS_LABEL_POSITION, 0, SvtSaveOptions::ODFSVER_012 ), + MAP_FULL( "MarkPosition", CHART, XML_TICK_MARK_POSITION, XML_SCH_TYPE_TICK_MARK_POSITION, 0, SvtSaveOptions::ODFSVER_012 ), + + // statistical properties + + MAP_ENTRY( "MeanValue", CHART, XML_MEAN_VALUE, XML_TYPE_BOOL ), + MAP_ENTRY( "ErrorMargin", CHART, XML_ERROR_MARGIN, XML_TYPE_DOUBLE ), + MAP_ENTRY( "PositiveError", CHART, XML_ERROR_LOWER_LIMIT, XML_TYPE_DOUBLE), + MAP_ENTRY( "NegativeError", CHART, XML_ERROR_UPPER_LIMIT, XML_TYPE_DOUBLE), + MAP_ENTRY( "ShowPositiveError", CHART, XML_ERROR_UPPER_INDICATOR, XML_TYPE_BOOL), + MAP_ENTRY( "ShowNegativeError", CHART, XML_ERROR_LOWER_INDICATOR, XML_TYPE_BOOL), + MAP_ENTRY( "ErrorBarStyle", CHART, XML_ERROR_CATEGORY, XML_SCH_TYPE_ERROR_BAR_STYLE ), + MAP_ENTRY( "PercentageError", CHART, XML_ERROR_PERCENTAGE, XML_TYPE_DOUBLE ), + + // regression curve properties + MAP_SPECIAL( "RegressionType", CHART, XML_REGRESSION_TYPE, XML_TYPE_STRING, XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE ), + MAP_SPECIAL_ODF13( "MovingAverageType", LO_EXT, XML_REGRESSION_MOVING_TYPE, XML_TYPE_STRING, XML_SCH_CONTEXT_SPECIAL_MOVING_AVERAGE_TYPE ), + MAP_SPECIAL_ODF13( "MovingAverageType", CHART, XML_REGRESSION_MOVING_TYPE, XML_TYPE_STRING, XML_SCH_CONTEXT_SPECIAL_MOVING_AVERAGE_TYPE ), + + // ODF 1.3 OFFICE-3958 + MAP_ENTRY_ODF13( "CurveName", XML_NAMESPACE_LO_EXT, XML_REGRESSION_CURVE_NAME, XML_TYPE_STRING ), + MAP_ENTRY_ODF13( "CurveName", XML_NAMESPACE_CHART, XML_REGRESSION_CURVE_NAME, XML_TYPE_STRING ), + MAP_ENTRY_ODF13( "PolynomialDegree", XML_NAMESPACE_LO_EXT, XML_REGRESSION_MAX_DEGREE, XML_TYPE_NUMBER ), + MAP_ENTRY_ODF13( "PolynomialDegree", XML_NAMESPACE_CHART, XML_REGRESSION_MAX_DEGREE, XML_TYPE_NUMBER ), + MAP_ENTRY_ODF13( "ForceIntercept", XML_NAMESPACE_LO_EXT, XML_REGRESSION_FORCE_INTERCEPT, XML_TYPE_BOOL ), + MAP_ENTRY_ODF13( "ForceIntercept", XML_NAMESPACE_CHART, XML_REGRESSION_FORCE_INTERCEPT, XML_TYPE_BOOL ), + MAP_ENTRY_ODF13( "InterceptValue", XML_NAMESPACE_LO_EXT, XML_REGRESSION_INTERCEPT_VALUE, XML_TYPE_DOUBLE ), + MAP_ENTRY_ODF13( "InterceptValue", XML_NAMESPACE_CHART, XML_REGRESSION_INTERCEPT_VALUE, XML_TYPE_DOUBLE ), + + // ODF 1.3 OFFICE-3959 + MAP_ENTRY_ODF13( "MovingAveragePeriod", XML_NAMESPACE_LO_EXT, XML_REGRESSION_PERIOD, XML_TYPE_NUMBER ), + MAP_ENTRY_ODF13( "MovingAveragePeriod", XML_NAMESPACE_CHART, XML_REGRESSION_PERIOD, XML_TYPE_NUMBER ), + + MAP_ENTRY_ODF_EXT( "ExtrapolateForward", LO_EXT, XML_REGRESSION_EXTRAPOLATE_FORWARD, XML_TYPE_DOUBLE ), + MAP_ENTRY_ODF_EXT( "ExtrapolateBackward", LO_EXT, XML_REGRESSION_EXTRAPOLATE_BACKWARD, XML_TYPE_DOUBLE ), + MAP_ENTRY_ODF_EXT_IMPORT( "ExtrapolateForward", CHART, XML_REGRESSION_EXTRAPOLATE_FORWARD, XML_TYPE_DOUBLE ), + MAP_ENTRY_ODF_EXT_IMPORT( "ExtrapolateBackward", CHART, XML_REGRESSION_EXTRAPOLATE_BACKWARD, XML_TYPE_DOUBLE ), + + MAP_ENTRY_ODF_EXT( "XName", LO_EXT, XML_REGRESSION_X_NAME, XML_TYPE_STRING ), + MAP_ENTRY_ODF_EXT( "YName", LO_EXT, XML_REGRESSION_Y_NAME, XML_TYPE_STRING ), + + MAP_SPECIAL_ODF12( "ErrorBarRangePositive", CHART, XML_ERROR_UPPER_RANGE, XML_TYPE_STRING, XML_SCH_CONTEXT_SPECIAL_ERRORBAR_RANGE ), // export only + MAP_SPECIAL_ODF12( "ErrorBarRangeNegative", CHART, XML_ERROR_LOWER_RANGE, XML_TYPE_STRING, XML_SCH_CONTEXT_SPECIAL_ERRORBAR_RANGE ), // export only + + // errorbars properties (chart2) + MAP_ENTRY_ODF_EXT( "Weight", LO_EXT, XML_ERROR_STANDARD_WEIGHT, XML_TYPE_DOUBLE), + MAP_ENTRY_ODF_EXT_IMPORT( "Weight", CHART, XML_ERROR_STANDARD_WEIGHT, XML_TYPE_DOUBLE), + + // series/data-point properties + MAP_SPECIAL( "DataCaption", CHART, XML_DATA_LABEL_NUMBER, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_NUMBER ), // convert one constant + MAP_SPECIAL( "DataCaption", CHART, XML_DATA_LABEL_TEXT, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_TEXT ), // to 'tristate' and two bools + MAP_SPECIAL( "DataCaption", CHART, XML_DATA_LABEL_SYMBOL, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SYMBOL ), + MAP_SPECIAL_ODF13( "DataCaption", CHART, XML_DATA_LABEL_SERIES, XML_TYPE_NUMBER | MID_FLAG_MERGE_PROPERTY, XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SERIES ), + MAP_SPECIAL_ODF12( "LabelSeparator", CHART, XML_LABEL_SEPARATOR, XML_TYPE_STRING | MID_FLAG_ELEMENT_ITEM, XML_SCH_CONTEXT_SPECIAL_LABEL_SEPARATOR ), + MAP_ENTRY_ODF12( "LabelPlacement", CHART, XML_LABEL_POSITION, XML_SCH_TYPE_LABEL_PLACEMENT_TYPE ), + MAP_ENTRY( "SegmentOffset", CHART, XML_PIE_OFFSET, XML_TYPE_NUMBER ), + MAP_SPECIAL_ODF12( "PercentageNumberFormat", STYLE, XML_PERCENTAGE_DATA_STYLE_NAME, XML_TYPE_NUMBER, XML_SCH_CONTEXT_SPECIAL_NUMBER_FORMAT ), + MAP_ENTRY_ODF_EXT( "ShowCustomLeaderLines", LO_EXT, XML_CUSTOM_LEADERLINES, XML_TYPE_BOOL ), + + // text properties for titles + MAP_SPECIAL( "TextRotation", STYLE, XML_ROTATION_ANGLE, XML_TYPE_NUMBER, XML_SCH_CONTEXT_SPECIAL_TEXT_ROTATION ), // convert 1/100th degrees to degrees + MAP_ENTRY( "StackedText", STYLE, XML_DIRECTION, XML_SCH_TYPE_TEXT_ORIENTATION ), + + // for compatibility to pre 6.0beta documents +// MAP_SPECIAL( "SymbolBitmap", CHART, XML_SYMBOL_IMAGE_NAME, XML_TYPE_STRING, XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE_NAME ), + + MAP_ENTRY( "ChartUserDefinedAttributes", TEXT, XML_XMLNS, XML_TYPE_ATTRIBUTE_CONTAINER | MID_FLAG_SPECIAL_ITEM ), + + MAP_ENTRY_END +}; + +// maps for enums to XML attributes + +const SvXMLEnumMapEntry<css::chart::ChartAxisLabelPosition> aXMLChartAxisLabelPositionEnumMap[] = +{ + { ::xmloff::token::XML_NEAR_AXIS, css::chart::ChartAxisLabelPosition_NEAR_AXIS }, + { ::xmloff::token::XML_NEAR_AXIS_OTHER_SIDE, css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE }, + { ::xmloff::token::XML_OUTSIDE_START, css::chart::ChartAxisLabelPosition_OUTSIDE_START }, + { ::xmloff::token::XML_OUTSIDE_END, css::chart::ChartAxisLabelPosition_OUTSIDE_END }, + { ::xmloff::token::XML_OUTSIDE_MINIMUM, css::chart::ChartAxisLabelPosition_OUTSIDE_START },//#i114142# + { ::xmloff::token::XML_OUTSIDE_MAXIMUM, css::chart::ChartAxisLabelPosition_OUTSIDE_END },//#i114142# + { ::xmloff::token::XML_TOKEN_INVALID, css::chart::ChartAxisLabelPosition(0) } +}; + +const SvXMLEnumMapEntry<css::chart::ChartAxisMarkPosition> aXMLChartAxisMarkPositionEnumMap[] = +{ + { ::xmloff::token::XML_AT_LABELS, css::chart::ChartAxisMarkPosition_AT_LABELS }, + { ::xmloff::token::XML_AT_AXIS, css::chart::ChartAxisMarkPosition_AT_AXIS }, + { ::xmloff::token::XML_AT_LABELS_AND_AXIS, css::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS }, + { ::xmloff::token::XML_TOKEN_INVALID, css::chart::ChartAxisMarkPosition(0) } +}; + +const SvXMLEnumMapEntry<css::chart::ChartAxisArrangeOrderType> aXMLChartAxisArrangementEnumMap[] = +{ + { ::xmloff::token::XML_SIDE_BY_SIDE, css::chart::ChartAxisArrangeOrderType_SIDE_BY_SIDE }, + { ::xmloff::token::XML_STAGGER_EVEN, css::chart::ChartAxisArrangeOrderType_STAGGER_EVEN }, + { ::xmloff::token::XML_STAGGER_ODD, css::chart::ChartAxisArrangeOrderType_STAGGER_ODD }, + { ::xmloff::token::XML_TOKEN_INVALID, css::chart::ChartAxisArrangeOrderType(0) } +}; + +const SvXMLEnumMapEntry<sal_Int32> aXMLChartErrorBarStyleEnumMap[] = +{ + { ::xmloff::token::XML_NONE, css::chart::ErrorBarStyle::NONE }, + { ::xmloff::token::XML_VARIANCE, css::chart::ErrorBarStyle::VARIANCE }, + { ::xmloff::token::XML_STANDARD_DEVIATION, css::chart::ErrorBarStyle::STANDARD_DEVIATION }, + { ::xmloff::token::XML_CONSTANT, css::chart::ErrorBarStyle::ABSOLUTE }, + { ::xmloff::token::XML_PERCENTAGE, css::chart::ErrorBarStyle::RELATIVE }, + { ::xmloff::token::XML_ERROR_MARGIN, css::chart::ErrorBarStyle::ERROR_MARGIN }, + { ::xmloff::token::XML_STANDARD_ERROR, css::chart::ErrorBarStyle::STANDARD_ERROR }, + { ::xmloff::token::XML_CELL_RANGE, css::chart::ErrorBarStyle::FROM_DATA }, + { ::xmloff::token::XML_TOKEN_INVALID, 0 } +}; + +const SvXMLEnumMapEntry<sal_Int32> aXMLChartSolidTypeEnumMap[] = +{ + { ::xmloff::token::XML_CUBOID, css::chart::ChartSolidType::RECTANGULAR_SOLID }, + { ::xmloff::token::XML_CYLINDER, css::chart::ChartSolidType::CYLINDER }, + { ::xmloff::token::XML_CONE, css::chart::ChartSolidType::CONE }, + { ::xmloff::token::XML_PYRAMID, css::chart::ChartSolidType::PYRAMID }, + { ::xmloff::token::XML_TOKEN_INVALID, 0 } +}; + +const SvXMLEnumMapEntry<css::chart::ChartDataRowSource> aXMLChartDataRowSourceTypeEnumMap[] = +{ + { ::xmloff::token::XML_COLUMNS, css::chart::ChartDataRowSource_COLUMNS }, + { ::xmloff::token::XML_ROWS, css::chart::ChartDataRowSource_ROWS }, + { ::xmloff::token::XML_TOKEN_INVALID, css::chart::ChartDataRowSource(0) } +}; + +const SvXMLEnumMapEntry<sal_Int32> g_XMLChartInterpolationTypeEnumMap_ODF12[] = +{ + // this is neither an enum nor a constants group, but just a + // documented long property + { ::xmloff::token::XML_NONE, 0 }, + { ::xmloff::token::XML_CUBIC_SPLINE, 1 }, + { ::xmloff::token::XML_B_SPLINE, 2 }, + { ::xmloff::token::XML_TOKEN_INVALID, 0 } +}; + +const SvXMLEnumMapEntry<sal_Int32> g_XMLChartInterpolationTypeEnumMap[] = +{ + // this is neither an enum nor a constants group, but just a + // documented long property + { ::xmloff::token::XML_NONE, 0 }, + { ::xmloff::token::XML_CUBIC_SPLINE, 1 }, + { ::xmloff::token::XML_B_SPLINE, 2 }, + { ::xmloff::token::XML_STEP_START, 3 }, + { ::xmloff::token::XML_STEP_END, 4 }, + { ::xmloff::token::XML_STEP_CENTER_X, 5 }, + { ::xmloff::token::XML_STEP_CENTER_Y, 6 }, + // the GNM values should only be used for reading Gnumeric ods files + // they should never be used for writing ods file + { ::xmloff::token::XML_GNM_STEP_START, 3 }, + { ::xmloff::token::XML_GNM_STEP_END, 4 }, + { ::xmloff::token::XML_GNM_STEP_CENTER_X, 5 }, + { ::xmloff::token::XML_GNM_STEP_CENTER_Y, 6 }, + { ::xmloff::token::XML_TOKEN_INVALID, 0 } +}; + +const SvXMLEnumMapEntry<sal_Int32> aXMLChartDataLabelPlacementEnumMap[] = +{ + { ::xmloff::token::XML_AVOID_OVERLAP, css::chart::DataLabelPlacement::AVOID_OVERLAP }, + { ::xmloff::token::XML_CENTER, css::chart::DataLabelPlacement::CENTER }, + { ::xmloff::token::XML_TOP, css::chart::DataLabelPlacement::TOP }, + { ::xmloff::token::XML_TOP_LEFT, css::chart::DataLabelPlacement::TOP_LEFT }, + { ::xmloff::token::XML_LEFT, css::chart::DataLabelPlacement::LEFT }, + { ::xmloff::token::XML_BOTTOM_LEFT, css::chart::DataLabelPlacement::BOTTOM_LEFT }, + { ::xmloff::token::XML_BOTTOM, css::chart::DataLabelPlacement::BOTTOM }, + { ::xmloff::token::XML_BOTTOM_RIGHT, css::chart::DataLabelPlacement::BOTTOM_RIGHT }, + { ::xmloff::token::XML_RIGHT, css::chart::DataLabelPlacement::RIGHT }, + { ::xmloff::token::XML_TOP_RIGHT, css::chart::DataLabelPlacement::TOP_RIGHT }, + { ::xmloff::token::XML_INSIDE, css::chart::DataLabelPlacement::INSIDE }, + { ::xmloff::token::XML_OUTSIDE, css::chart::DataLabelPlacement::OUTSIDE }, + { ::xmloff::token::XML_NEAR_ORIGIN, css::chart::DataLabelPlacement::NEAR_ORIGIN }, + { ::xmloff::token::XML_TOKEN_INVALID, 0 } +}; + +const SvXMLEnumMapEntry<sal_Int32> aXMLChartMissingValueTreatmentEnumMap[] = +{ + { ::xmloff::token::XML_LEAVE_GAP, css::chart::MissingValueTreatment::LEAVE_GAP }, + { ::xmloff::token::XML_USE_ZERO, css::chart::MissingValueTreatment::USE_ZERO }, + { ::xmloff::token::XML_IGNORE, css::chart::MissingValueTreatment::CONTINUE }, + { ::xmloff::token::XML_TOKEN_INVALID,0 }, +}; + +namespace { + +SvXMLEnumMapEntry<drawing::LineStyle> const aLineStyleMap[] = +{ + { XML_NONE, drawing::LineStyle_NONE }, + { XML_SOLID, drawing::LineStyle_SOLID }, + { XML_DASH, drawing::LineStyle_DASH }, + { XML_TOKEN_INVALID, drawing::LineStyle(0) } +}; + +SvXMLEnumMapEntry<drawing::FillStyle> const aFillStyleMap[] = +{ + { XML_NONE, drawing::FillStyle_NONE }, + { XML_SOLID, drawing::FillStyle_SOLID }, + { XML_HATCH, drawing::FillStyle_HATCH } +}; + +} + +// the following class implementations are in this file: + +// * XMLChartPropHdlFactory +// * XMLChartPropertySetMapper +// * XMLChartExportPropertyMapper +// * XMLChartImportPropertyMapper +// * SchXMLStyleExport + +XMLChartPropHdlFactory::XMLChartPropHdlFactory(SvXMLExport const*const pExport) + : m_pExport(pExport) +{ +} + +XMLChartPropHdlFactory::~XMLChartPropHdlFactory() +{ +} + +const XMLPropertyHandler* XMLChartPropHdlFactory::GetPropertyHandler( sal_Int32 nType ) const +{ + const XMLPropertyHandler* pHdl = XMLPropertyHandlerFactory::GetPropertyHandler( nType ); + if( ! pHdl ) + { + switch( nType ) + { + case XML_SCH_TYPE_AXIS_POSITION: + pHdl = new XMLAxisPositionPropertyHdl( false ); + break; + case XML_SCH_TYPE_AXIS_POSITION_VALUE: + pHdl = new XMLAxisPositionPropertyHdl( true ); + break; + + case XML_SCH_TYPE_AXIS_LABEL_POSITION: + pHdl = new XMLEnumPropertyHdl( aXMLChartAxisLabelPositionEnumMap); + break; + + case XML_SCH_TYPE_TICK_MARK_POSITION: + pHdl = new XMLEnumPropertyHdl( aXMLChartAxisMarkPositionEnumMap); + break; + + case XML_SCH_TYPE_AXIS_ARRANGEMENT: + pHdl = new XMLEnumPropertyHdl( aXMLChartAxisArrangementEnumMap); + break; + + case XML_SCH_TYPE_ERROR_BAR_STYLE: + // here we have a constant rather than an enum + pHdl = new XMLErrorBarStylePropertyHdl( aXMLChartErrorBarStyleEnumMap ); + break; + + case XML_SCH_TYPE_ERROR_INDICATOR_LOWER: + pHdl = new XMLErrorIndicatorPropertyHdl( false ); + break; + case XML_SCH_TYPE_ERROR_INDICATOR_UPPER: + pHdl = new XMLErrorIndicatorPropertyHdl( true ); + break; + + case XML_SCH_TYPE_SOLID_TYPE: + // here we have a constant rather than an enum + pHdl = new XMLEnumPropertyHdl( aXMLChartSolidTypeEnumMap ); + break; + case XML_SCH_TYPE_LABEL_PLACEMENT_TYPE: + // here we have a constant rather than an enum + pHdl = new XMLEnumPropertyHdl( aXMLChartDataLabelPlacementEnumMap ); + break; + case XML_SCH_TYPE_DATAROWSOURCE: + pHdl = new XMLEnumPropertyHdl( aXMLChartDataRowSourceTypeEnumMap); + break; + case XML_SCH_TYPE_TEXT_ORIENTATION: + pHdl = new XMLTextOrientationHdl; + break; + + case XML_SCH_TYPE_INTERPOLATION: + if (m_pExport && m_pExport->getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_013) + { + pHdl = new XMLEnumPropertyHdl(g_XMLChartInterpolationTypeEnumMap_ODF12); + } + else // ODF 1.3 OFFICE-3662 + { + pHdl = new XMLEnumPropertyHdl(g_XMLChartInterpolationTypeEnumMap); + } + break; + case XML_SCH_TYPE_SYMBOL_TYPE: + pHdl = new XMLSymbolTypePropertyHdl( false ); + break; + + case XML_SCH_TYPE_NAMED_SYMBOL: + pHdl = new XMLSymbolTypePropertyHdl( true ); + break; + + case XML_SCH_TYPE_MISSING_VALUE_TREATMENT: + pHdl = new XMLEnumPropertyHdl( aXMLChartMissingValueTreatmentEnumMap ); + break; + case XML_SCH_TYPE_LABEL_BORDER_STYLE: + pHdl = new XMLEnumPropertyHdl( aLineStyleMap ); + break; + case XML_SCH_TYPE_LABEL_BORDER_OPACITY: + pHdl = new XMLOpacityPropertyHdl(nullptr); + break; + case XML_SCH_TYPE_LABEL_FILL_STYLE: + pHdl = new XMLEnumPropertyHdl( aFillStyleMap ); + break; + default: + ; + } + if( pHdl ) + PutHdlCache( nType, pHdl ); + } + + return pHdl; +} + +XMLChartPropertySetMapper::XMLChartPropertySetMapper(SvXMLExport const*const pExport) + : XMLPropertySetMapper(aXMLChartPropMap, new XMLChartPropHdlFactory(pExport), pExport != nullptr) +{ +} + +XMLChartPropertySetMapper::~XMLChartPropertySetMapper() +{ +} + +XMLChartExportPropertyMapper::XMLChartExportPropertyMapper( const rtl::Reference< XMLPropertySetMapper >& rMapper, + SvXMLExport& rExport) : + SvXMLExportPropertyMapper( rMapper ), + mrExport( rExport ) +{ + // chain draw properties + ChainExportMapper( XMLShapeExport::CreateShapePropMapper( rExport )); + + // chain text properties + ChainExportMapper( XMLTextParagraphExport::CreateParaExtPropMapper( rExport )); +} + +XMLChartExportPropertyMapper::~XMLChartExportPropertyMapper() +{ +} + +void XMLChartExportPropertyMapper::ContextFilter( + bool bEnableFoFontFamily, + std::vector< XMLPropertyState >& rProperties, + const uno::Reference< beans::XPropertySet >& rPropSet ) const +{ + OUString aAutoPropName; + bool bCheckAuto = false; + + // filter properties + for( auto& rProperty : rProperties ) + { + // find properties with context + // to prevent writing this property set mnIndex member to -1 + switch( getPropertySetMapper()->GetEntryContextId( rProperty.mnIndex )) + { + // if Auto... is set the corresponding properties mustn't be exported + case XML_SCH_CONTEXT_MIN: + bCheckAuto = true; + aAutoPropName = "AutoMin"; + break; + case XML_SCH_CONTEXT_MAX: + bCheckAuto = true; + aAutoPropName = "AutoMax"; + break; + case XML_SCH_CONTEXT_STEP_MAIN: + bCheckAuto = true; + aAutoPropName = "AutoStepMain"; + break; + case XML_SCH_CONTEXT_STEP_HELP_COUNT: + bCheckAuto = true; + aAutoPropName = "AutoStepHelp"; + break; + + case XML_SCH_CONTEXT_ORIGIN: + bCheckAuto = true; + aAutoPropName = "AutoOrigin"; + break; + + // the following property is deprecated + // element-item symbol-image is used now + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE_NAME: + rProperty.mnIndex = -1; + break; + + case XML_SCH_CONTEXT_STOCK_WITH_VOLUME: + case XML_SCH_CONTEXT_LINES_USED: + // note this avoids export of the properties in OASIS format, + // but also for the OOo XML Flat format (used by binfilter), + // because there, the transformation to OOo is done after the + // complete export of the chart in OASIS format. + if( mrExport.getExportFlags() & SvXMLExportFlags::OASIS ) + rProperty.mnIndex = -1; + break; + } + + if( bCheckAuto ) + { + if( rPropSet.is()) + { + try + { + bool bAuto = false; + uno::Any aAny = rPropSet->getPropertyValue( aAutoPropName ); + aAny >>= bAuto; + if( bAuto ) + rProperty.mnIndex = -1; + } + catch(const beans::UnknownPropertyException&) + { + } + } + bCheckAuto = false; + } + } + + SvXMLExportPropertyMapper::ContextFilter(bEnableFoFontFamily, rProperties, rPropSet); +} + +void XMLChartExportPropertyMapper::handleElementItem( + SvXMLExport& rExport, + const XMLPropertyState& rProperty, SvXmlExportFlags nFlags, + const ::std::vector< XMLPropertyState > *pProperties, + sal_uInt32 nIdx ) const +{ + switch( getPropertySetMapper()->GetEntryContextId( rProperty.mnIndex )) + { + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE: + { + uno::Reference<graphic::XGraphic> xGraphic; + rProperty.maValue >>= xGraphic; + + OUString sInternalURL; + // export as XLink reference into the package + // if embedding is off + if (xGraphic.is()) + { + OUString aOutMimeType; + sInternalURL = mrExport.AddEmbeddedXGraphic(xGraphic, aOutMimeType); + } + if (!sInternalURL.isEmpty()) + { + mrExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, sInternalURL); + } + + { + sal_uInt32 nPropIndex = rProperty.mnIndex; + // this is the element that has to live until the next statement + SvXMLElementExport aElem( mrExport, + getPropertySetMapper()->GetEntryNameSpace( nPropIndex ), + getPropertySetMapper()->GetEntryXMLName( nPropIndex ), + true, true ); + + // export as Base64 embedded graphic + // if embedding is on + if (xGraphic.is()) + mrExport.AddEmbeddedXGraphicAsBase64(xGraphic); + } + } + break; + + case XML_SCH_CONTEXT_SPECIAL_LABEL_SEPARATOR: + { + OUString aSeparator; + rProperty.maValue >>= aSeparator; + + if( !aSeparator.isEmpty() ) + { + sal_uInt32 nPropIndex = rProperty.mnIndex; + SvXMLElementExport aElem( mrExport, + getPropertySetMapper()->GetEntryNameSpace( nPropIndex ), + getPropertySetMapper()->GetEntryXMLName( nPropIndex ), + true, true ); + + SchXMLTools::exportText( mrExport, aSeparator, true ); + } + } + break; + + default: + // call parent + SvXMLExportPropertyMapper::handleElementItem( rExport, rProperty, + nFlags, pProperties, nIdx ); + break; + } +} + +namespace { + +OUString convertRange( const OUString & rRange, const uno::Reference< chart2::XChartDocument > & xDoc ) +{ + OUString aResult = rRange; + if( !xDoc.is() ) + return aResult; + uno::Reference< chart2::data::XRangeXMLConversion > xConversion( + xDoc->getDataProvider(), uno::UNO_QUERY ); + try + { + if( xConversion.is()) + aResult = xConversion->convertRangeToXML( rRange ); + } + catch (css::lang::IllegalArgumentException&) + { + } + + return aResult; +} + +} + +void XMLChartExportPropertyMapper::handleSpecialItem( + SvXMLAttributeList& rAttrList, const XMLPropertyState& rProperty, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap, + const ::std::vector< XMLPropertyState > *pProperties, + sal_uInt32 nIdx ) const +{ + bool bHandled = false; + + sal_Int32 nContextId = getPropertySetMapper()->GetEntryContextId( rProperty.mnIndex ); + + if( nContextId ) + { + bHandled = true; + + OUString sAttrName = getPropertySetMapper()->GetEntryXMLName( rProperty.mnIndex ); + sal_uInt16 nNameSpace = getPropertySetMapper()->GetEntryNameSpace( rProperty.mnIndex ); + OUStringBuffer sValueBuffer; + + sal_Int32 nValue = 0; + bool bValue = false; + + switch( nContextId ) + { + case XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_INNER: + case XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_INNER: + rProperty.maValue >>= nValue; + bValue = (( nValue & chart::ChartAxisMarks::INNER ) == chart::ChartAxisMarks::INNER ); + ::sax::Converter::convertBool( sValueBuffer, bValue ); + break; + case XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_OUTER: + case XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_OUTER: + rProperty.maValue >>= nValue; + bValue = (( nValue & chart::ChartAxisMarks::OUTER ) == chart::ChartAxisMarks::OUTER ); + ::sax::Converter::convertBool( sValueBuffer, bValue ); + break; + case XML_SCH_CONTEXT_SPECIAL_TEXT_ROTATION: + { + // convert from 100th degrees to degrees (double) + rProperty.maValue >>= nValue; + double fVal = static_cast<double>(nValue) / 100.0; + ::sax::Converter::convertDouble( sValueBuffer, fVal ); + } + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_NUMBER: + { + rProperty.maValue >>= nValue; + if( ( nValue & chart::ChartDataCaption::VALUE ) == chart::ChartDataCaption::VALUE ) + { + if( ( nValue & chart::ChartDataCaption::PERCENT ) == chart::ChartDataCaption::PERCENT ) + { + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentVersion < SvtSaveOptions::ODFSVER_012) + sValueBuffer.append( GetXMLToken( XML_PERCENTAGE )); + else + sValueBuffer.append( GetXMLToken( XML_VALUE_AND_PERCENTAGE )); + } + else + sValueBuffer.append( GetXMLToken( XML_VALUE )); + } + else if(( nValue & chart::ChartDataCaption::PERCENT ) == chart::ChartDataCaption::PERCENT ) + sValueBuffer.append( GetXMLToken( XML_PERCENTAGE )); + else + sValueBuffer.append( GetXMLToken( XML_NONE )); + } + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_TEXT: + rProperty.maValue >>= nValue; + bValue = (( nValue & chart::ChartDataCaption::TEXT ) == chart::ChartDataCaption::TEXT ); + ::sax::Converter::convertBool( sValueBuffer, bValue ); + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SYMBOL: + rProperty.maValue >>= nValue; + bValue = (( nValue & chart::ChartDataCaption::SYMBOL ) == chart::ChartDataCaption::SYMBOL ); + ::sax::Converter::convertBool( sValueBuffer, bValue ); + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SERIES: + rProperty.maValue >>= nValue; + bValue = (( nValue & chart::ChartDataCaption::DATA_SERIES ) == chart::ChartDataCaption::DATA_SERIES ); + ::sax::Converter::convertBool( sValueBuffer, bValue ); + break; + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_WIDTH: + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_HEIGHT: + { + awt::Size aSize; + rProperty.maValue >>= aSize; + rUnitConverter.convertMeasureToXML( sValueBuffer, + nContextId == XML_SCH_CONTEXT_SPECIAL_SYMBOL_WIDTH + ? aSize.Width + : aSize.Height ); + } + break; + + case XML_SCH_CONTEXT_SPECIAL_NUMBER_FORMAT: + { + // just for import + break; + } + + case XML_SCH_CONTEXT_SPECIAL_ERRORBAR_RANGE: + { + OUString aRangeStr; + rProperty.maValue >>= aRangeStr; + sValueBuffer.append(convertRange(aRangeStr, mxChartDoc)); + } + break; + case XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE: + { + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion( + mrExport.getSaneDefaultVersion()); + + OUString aServiceName; + rProperty.maValue >>= aServiceName; + if (aServiceName == "com.sun.star.chart2.LinearRegressionCurve") + sValueBuffer.append( GetXMLToken( XML_LINEAR )); + else if (aServiceName == "com.sun.star.chart2.LogarithmicRegressionCurve") + sValueBuffer.append( GetXMLToken( XML_LOGARITHMIC )); + else if (aServiceName == "com.sun.star.chart2.ExponentialRegressionCurve") + sValueBuffer.append( GetXMLToken( XML_EXPONENTIAL )); + else if (aServiceName == "com.sun.star.chart2.PotentialRegressionCurve") + sValueBuffer.append( GetXMLToken( XML_POWER )); + else if (nCurrentVersion >= SvtSaveOptions::ODFSVER_013 && aServiceName == "com.sun.star.chart2.PolynomialRegressionCurve") + { // ODF 1.3 OFFICE-3958 + sValueBuffer.append( GetXMLToken( XML_POLYNOMIAL )); + } + else if (nCurrentVersion >= SvtSaveOptions::ODFSVER_013 && aServiceName == "com.sun.star.chart2.MovingAverageRegressionCurve") + { // ODF 1.3 OFFICE-3959 + sValueBuffer.append( GetXMLToken( XML_MOVING_AVERAGE )); + } + } + break; + + case XML_SCH_CONTEXT_SPECIAL_MOVING_AVERAGE_TYPE: + { + rProperty.maValue >>= nValue; + if (nValue == MovingAverageType::Prior) + sValueBuffer.append( GetXMLToken( XML_PRIOR )); + else if (nValue == MovingAverageType::Central) + sValueBuffer.append( GetXMLToken( XML_CENTRAL )); + else if (nValue == MovingAverageType::AveragedAbscissa) + sValueBuffer.append( GetXMLToken( XML_AVERAGED_ABSCISSA )); + else // default + sValueBuffer.append( GetXMLToken( XML_PRIOR )); + } + break; + + default: + bHandled = false; + break; + } + + if( !sValueBuffer.isEmpty()) + { + OUString sValue = sValueBuffer.makeStringAndClear(); + sAttrName = rNamespaceMap.GetQNameByKey( nNameSpace, sAttrName ); + rAttrList.AddAttribute( sAttrName, sValue ); + } + } + + if( !bHandled ) + { + // call parent + SvXMLExportPropertyMapper::handleSpecialItem( rAttrList, rProperty, rUnitConverter, rNamespaceMap, pProperties, nIdx ); + } +} + +void XMLChartExportPropertyMapper::setChartDoc( const uno::Reference< chart2::XChartDocument >& xChartDoc ) +{ + mxChartDoc = xChartDoc; +} + +XMLChartImportPropertyMapper::XMLChartImportPropertyMapper( const rtl::Reference< XMLPropertySetMapper >& rMapper, + const SvXMLImport& _rImport ) : + SvXMLImportPropertyMapper( rMapper, const_cast< SvXMLImport & >( _rImport )), + mrImport( const_cast< SvXMLImport & > ( _rImport )) +{ + // chain shape mapper for drawing properties + + // give an empty model. It is only used for numbering rules that don't exist in chart + uno::Reference< frame::XModel > xEmptyModel; + ChainImportMapper( XMLShapeImportHelper::CreateShapePropMapper( xEmptyModel, mrImport )); + + //#i14365# save and load writing-mode for chart elements + //The property TextWritingMode is mapped wrongly in the underlying draw mapper, but for draw it is necessary + //We remove that property here only for chart thus the chart can use the correct mapping from the writer paragraph settings (attribute 'writing-mode' <-> property 'WritingMode') + sal_Int32 nUnwantedWrongEntry = maPropMapper->FindEntryIndex( "TextWritingMode", XML_NAMESPACE_STYLE, GetXMLToken(XML_WRITING_MODE) ); + maPropMapper->RemoveEntry(nUnwantedWrongEntry); + + // do not chain text properties: on import this is done by shape mapper + // to import old documents +} + +XMLChartImportPropertyMapper::~XMLChartImportPropertyMapper() +{ +} + +bool XMLChartImportPropertyMapper::handleSpecialItem( + XMLPropertyState& rProperty, + ::std::vector< XMLPropertyState >& rProperties, + const OUString& rValue, + const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap ) const +{ + sal_Int32 nContextId = maPropMapper->GetEntryContextId( rProperty.mnIndex ); + bool bRet = (nContextId != 0); + + if( nContextId ) + { + sal_Int32 nValue = 0; + bool bValue = false; + + switch( nContextId ) + { + case XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_INNER: + case XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_INNER: + (void)::sax::Converter::convertBool( bValue, rValue ); + // modify old value + rProperty.maValue >>= nValue; + if( bValue ) + SCH_XML_SETFLAG( nValue, chart::ChartAxisMarks::INNER ); + else + SCH_XML_UNSETFLAG( nValue, chart::ChartAxisMarks::INNER ); + rProperty.maValue <<= nValue; + break; + case XML_SCH_CONTEXT_SPECIAL_TICKS_MAJ_OUTER: + case XML_SCH_CONTEXT_SPECIAL_TICKS_MIN_OUTER: + (void)::sax::Converter::convertBool( bValue, rValue ); + // modify old value + rProperty.maValue >>= nValue; + if( bValue ) + SCH_XML_SETFLAG( nValue, chart::ChartAxisMarks::OUTER ); + else + SCH_XML_UNSETFLAG( nValue, chart::ChartAxisMarks::OUTER ); + rProperty.maValue <<= nValue; + break; + case XML_SCH_CONTEXT_SPECIAL_TEXT_ROTATION: + { + // convert from degrees (double) to 100th degrees (integer) + double fVal; + ::sax::Converter::convertDouble( fVal, rValue ); + nValue = static_cast<sal_Int32>( fVal * 100.0 ); + rProperty.maValue <<= nValue; + } + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_NUMBER: + { + // modify old value + rProperty.maValue >>= nValue; + if( IsXMLToken( rValue, XML_NONE )) + SCH_XML_UNSETFLAG( nValue, chart::ChartDataCaption::VALUE | chart::ChartDataCaption::PERCENT ); + else if( IsXMLToken( rValue, XML_VALUE_AND_PERCENTAGE ) ) + SCH_XML_SETFLAG( nValue, chart::ChartDataCaption::VALUE | chart::ChartDataCaption::PERCENT ); + else if( IsXMLToken( rValue, XML_VALUE ) ) + SCH_XML_SETFLAG( nValue, chart::ChartDataCaption::VALUE ); + else // must be XML_PERCENTAGE + SCH_XML_SETFLAG( nValue, chart::ChartDataCaption::PERCENT ); + rProperty.maValue <<= nValue; + } + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_TEXT: + rProperty.maValue >>= nValue; + (void)::sax::Converter::convertBool( bValue, rValue ); + if( bValue ) + SCH_XML_SETFLAG( nValue, chart::ChartDataCaption::TEXT ); + else + SCH_XML_UNSETFLAG( nValue, chart::ChartDataCaption::TEXT ); + rProperty.maValue <<= nValue; + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SYMBOL: + rProperty.maValue >>= nValue; + (void)::sax::Converter::convertBool( bValue, rValue ); + if( bValue ) + SCH_XML_SETFLAG( nValue, chart::ChartDataCaption::SYMBOL ); + else + SCH_XML_UNSETFLAG( nValue, chart::ChartDataCaption::SYMBOL ); + rProperty.maValue <<= nValue; + break; + case XML_SCH_CONTEXT_SPECIAL_DATA_LABEL_SERIES: + rProperty.maValue >>= nValue; + (void)::sax::Converter::convertBool( bValue, rValue ); + if( bValue ) + SCH_XML_SETFLAG( nValue, chart::ChartDataCaption::DATA_SERIES ); + else + SCH_XML_UNSETFLAG( nValue, chart::ChartDataCaption::DATA_SERIES ); + rProperty.maValue <<= nValue; + break; + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_WIDTH: + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_HEIGHT: + { + awt::Size aSize; + rProperty.maValue >>= aSize; + rUnitConverter.convertMeasureToCore( + (nContextId == XML_SCH_CONTEXT_SPECIAL_SYMBOL_WIDTH) + ? aSize.Width + : aSize.Height, + rValue ); + rProperty.maValue <<= aSize; + } + break; + + case XML_SCH_CONTEXT_SPECIAL_ERRORBAR_RANGE: + { + rProperty.maValue <<= rValue; + } + break; + + // deprecated from 6.0 beta on + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE_NAME: + rProperty.maValue <<= mrImport.loadGraphicByURL(rValue); + break; + + case XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE: + { + if (IsXMLToken( rValue, XML_LINEAR )) + rProperty.maValue <<= OUString("com.sun.star.chart2.LinearRegressionCurve"); + else if (IsXMLToken( rValue, XML_LOGARITHMIC)) + rProperty.maValue <<= OUString("com.sun.star.chart2.LogarithmicRegressionCurve"); + else if (IsXMLToken( rValue, XML_EXPONENTIAL)) + rProperty.maValue <<= OUString("com.sun.star.chart2.ExponentialRegressionCurve"); + else if (IsXMLToken( rValue, XML_POWER)) + rProperty.maValue <<= OUString("com.sun.star.chart2.PotentialRegressionCurve"); + else if (IsXMLToken( rValue, XML_POLYNOMIAL)) + rProperty.maValue <<= OUString("com.sun.star.chart2.PolynomialRegressionCurve"); + else if (IsXMLToken( rValue, XML_MOVING_AVERAGE)) + rProperty.maValue <<= OUString("com.sun.star.chart2.MovingAverageRegressionCurve"); + } + break; + + case XML_SCH_CONTEXT_SPECIAL_MOVING_AVERAGE_TYPE: + { + if (IsXMLToken( rValue, XML_PRIOR )) + rProperty.maValue <<= MovingAverageType::Prior; + else if (IsXMLToken( rValue, XML_CENTRAL)) + rProperty.maValue <<= MovingAverageType::Central; + else if (IsXMLToken( rValue, XML_AVERAGED_ABSCISSA)) + rProperty.maValue <<= MovingAverageType::AveragedAbscissa; + else // default + rProperty.maValue <<= MovingAverageType::Prior; + } + break; + + default: + bRet = false; + break; + } + } + + // if we didn't handle it, the parent should + if( !bRet ) + { + // call parent + bRet = SvXMLImportPropertyMapper::handleSpecialItem( rProperty, rProperties, rValue, rUnitConverter, rNamespaceMap ); + } + + return bRet; +} + +void XMLChartImportPropertyMapper::finished( ::std::vector< XMLPropertyState >& /*rProperties*/, sal_Int32 /*nStartIndex*/, sal_Int32 /*nEndIndex*/ ) const +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLAutoStylePoolP.cxx b/xmloff/source/chart/SchXMLAutoStylePoolP.cxx new file mode 100644 index 000000000..d60b3de72 --- /dev/null +++ b/xmloff/source/chart/SchXMLAutoStylePoolP.cxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <SchXMLAutoStylePoolP.hxx> +#include "PropertyMap.hxx" +#include <SchXMLExport.hxx> +#include <xmloff/families.hxx> +#include <xmloff/namespacemap.hxx> + + +SchXMLAutoStylePoolP::SchXMLAutoStylePoolP( SchXMLExport& rSchXMLExport ) : + SvXMLAutoStylePoolP( rSchXMLExport ), + mrSchXMLExport( rSchXMLExport ) +{} + +SchXMLAutoStylePoolP::~SchXMLAutoStylePoolP() +{} + +void SchXMLAutoStylePoolP::exportStyleAttributes( + SvXMLAttributeList& rAttrList, + XmlStyleFamily nFamily, + const ::std::vector< XMLPropertyState >& rProperties, + const SvXMLExportPropertyMapper& rPropExp + , const SvXMLUnitConverter& rUnitConverter, + const SvXMLNamespaceMap& rNamespaceMap + ) const +{ + SvXMLAutoStylePoolP::exportStyleAttributes( rAttrList, nFamily, rProperties, + rPropExp, rUnitConverter, rNamespaceMap ); + + if( nFamily != XmlStyleFamily::SCH_CHART_ID ) + return; + + for( const auto& rProp : rProperties ) + { + if( rProp.mnIndex == -1 ) + continue; + + rtl::Reference< XMLPropertySetMapper > aPropMapper = + mrSchXMLExport.GetPropertySetMapper(); + sal_Int16 nContextID = aPropMapper->GetEntryContextId( rProp.mnIndex ); + if( nContextID == XML_SCH_CONTEXT_SPECIAL_NUMBER_FORMAT ) + { + sal_Int32 nNumberFormat = -1; + if( ( rProp.maValue >>= nNumberFormat ) && + ( nNumberFormat != -1 )) + { + OUString sAttrValue = mrSchXMLExport.getDataStyleName( nNumberFormat ); + if( !sAttrValue.isEmpty() ) + { + mrSchXMLExport.AddAttribute( + aPropMapper->GetEntryNameSpace( rProp.mnIndex ), + aPropMapper->GetEntryXMLName( rProp.mnIndex ), + sAttrValue ); + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLAxisContext.cxx b/xmloff/source/chart/SchXMLAxisContext.cxx new file mode 100644 index 000000000..ad8b187f8 --- /dev/null +++ b/xmloff/source/chart/SchXMLAxisContext.cxx @@ -0,0 +1,900 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sax/tools/converter.hxx> + +#include "SchXMLAxisContext.hxx" +#include "SchXMLChartContext.hxx" +#include "SchXMLTools.hxx" +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlement.hxx> +#include <xmloff/xmlstyle.hxx> +#include <xmloff/prstylei.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmluconv.hxx> + +#include <rtl/math.hxx> +#include <tools/color.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/chart/ChartAxisLabelPosition.hpp> +#include <com/sun/star/chart/ChartAxisMarkPosition.hpp> +#include <com/sun/star/chart/ChartAxisPosition.hpp> +#include <com/sun/star/chart/ChartAxisType.hpp> +#include <com/sun/star/chart/TimeIncrement.hpp> +#include <com/sun/star/chart/TimeInterval.hpp> +#include <com/sun/star/chart/TimeUnit.hpp> +#include <com/sun/star/chart/XAxis.hpp> +#include <com/sun/star/chart/XAxisSupplier.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> + +#include <com/sun/star/drawing/LineStyle.hpp> + +using namespace ::xmloff::token; +using namespace com::sun::star; + +using com::sun::star::uno::Reference; + +const SvXMLEnumMapEntry<SchXMLAxisDimension> aXMLAxisDimensionMap[] = +{ + { XML_X, SCH_XML_AXIS_X }, + { XML_Y, SCH_XML_AXIS_Y }, + { XML_Z, SCH_XML_AXIS_Z }, + { XML_TOKEN_INVALID, SchXMLAxisDimension(0) } +}; + +const SvXMLEnumMapEntry<sal_uInt16> aXMLAxisTypeMap[] = +{ + { XML_AUTO, css::chart::ChartAxisType::AUTOMATIC }, + { XML_TEXT, css::chart::ChartAxisType::CATEGORY }, + { XML_DATE, css::chart::ChartAxisType::DATE }, + { XML_TOKEN_INVALID, 0 } +}; + +namespace { + +class SchXMLCategoriesContext : public SvXMLImportContext +{ +private: + OUString& mrAddress; + +public: + SchXMLCategoriesContext( SvXMLImport& rImport, + OUString& rAddress ); + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +class DateScaleContext : public SvXMLImportContext +{ +public: + DateScaleContext( SvXMLImport& rImport, + const Reference< beans::XPropertySet >& rAxisProps ); + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + +private: + Reference< beans::XPropertySet > m_xAxisProps; +}; + +} + +SchXMLAxisContext::SchXMLAxisContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + Reference< chart::XDiagram > const & xDiagram, + std::vector< SchXMLAxis >& rAxes, + OUString & rCategoriesAddress, + bool bAddMissingXAxisForNetCharts, + bool bAdaptWrongPercentScaleValues, + bool bAdaptXAxisOrientationForOld2DBarCharts, + bool& rbAxisPositionAttributeImported ) : + SvXMLImportContext( rImport ), + m_rImportHelper( rImpHelper ), + m_xDiagram( xDiagram ), + m_rAxes( rAxes ), + m_rCategoriesAddress( rCategoriesAddress ), + m_nAxisType(chart::ChartAxisType::AUTOMATIC), + m_bAxisTypeImported(false), + m_bDateScaleImported(false), + m_bAddMissingXAxisForNetCharts( bAddMissingXAxisForNetCharts ), + m_bAdaptWrongPercentScaleValues( bAdaptWrongPercentScaleValues ), + m_bAdaptXAxisOrientationForOld2DBarCharts( bAdaptXAxisOrientationForOld2DBarCharts ), + m_rbAxisPositionAttributeImported( rbAxisPositionAttributeImported ) +{ +} + +SchXMLAxisContext::~SchXMLAxisContext() +{} + +static Reference< chart::XAxis > lcl_getChartAxis(const SchXMLAxis& rCurrentAxis, const Reference< chart::XDiagram >& rDiagram ) +{ + Reference< chart::XAxis > xAxis; + Reference< chart::XAxisSupplier > xAxisSuppl( rDiagram, uno::UNO_QUERY ); + if( !xAxisSuppl.is() ) + return xAxis; + if( rCurrentAxis.nAxisIndex == 0 ) + xAxis = xAxisSuppl->getAxis(rCurrentAxis.eDimension); + else + xAxis = xAxisSuppl->getSecondaryAxis(rCurrentAxis.eDimension); + return xAxis; +} + +/* returns a shape for the current axis's title. The property + "Has...AxisTitle" is set to "True" to get the shape + */ +Reference< drawing::XShape > SchXMLAxisContext::getTitleShape() const +{ + Reference< drawing::XShape > xResult; + Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY ); + Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) ); + if( !xDiaProp.is() || !xAxis.is() ) + return xResult; + + OUString aPropName; + switch( m_aCurrentAxis.eDimension ) + { + case SCH_XML_AXIS_X: + if( m_aCurrentAxis.nAxisIndex == 0 ) + aPropName = "HasXAxisTitle"; + else + aPropName = "HasSecondaryXAxisTitle"; + break; + case SCH_XML_AXIS_Y: + if( m_aCurrentAxis.nAxisIndex == 0 ) + aPropName = "HasYAxisTitle"; + else + aPropName = "HasSecondaryYAxisTitle"; + break; + case SCH_XML_AXIS_Z: + aPropName = "HasZAxisTitle"; + break; + case SCH_XML_AXIS_UNDEF: + SAL_INFO("xmloff.chart", "Invalid axis" ); + break; + } + xDiaProp->setPropertyValue( aPropName, uno::Any(true) ); + xResult.set( xAxis->getAxisTitle(), uno::UNO_QUERY ); + return xResult; +} + +void SchXMLAxisContext::CreateGrid( const OUString& sAutoStyleName, bool bIsMajor ) +{ + Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY ); + Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) ); + if( !xDiaProp.is() || !xAxis.is() ) + return; + + OUString aPropName; + switch( m_aCurrentAxis.eDimension ) + { + case SCH_XML_AXIS_X: + if( bIsMajor ) + aPropName = "HasXAxisGrid"; + else + aPropName = "HasXAxisHelpGrid"; + break; + case SCH_XML_AXIS_Y: + if( bIsMajor ) + aPropName = "HasYAxisGrid"; + else + aPropName = "HasYAxisHelpGrid"; + break; + case SCH_XML_AXIS_Z: + if( bIsMajor ) + aPropName = "HasZAxisGrid"; + else + aPropName = "HasZAxisHelpGrid"; + break; + case SCH_XML_AXIS_UNDEF: + SAL_INFO("xmloff.chart", "Invalid axis" ); + break; + } + xDiaProp->setPropertyValue( aPropName, uno::Any(true) ); + + Reference< beans::XPropertySet > xGridProp; + if( bIsMajor ) + xGridProp = xAxis->getMajorGrid(); + else + xGridProp = xAxis->getMinorGrid(); + + // set properties + if( xGridProp.is()) + { + // the line color is black as default, in the model it is a light gray + xGridProp->setPropertyValue("LineColor", + uno::Any( COL_BLACK )); + if (!sAutoStyleName.isEmpty()) + m_rImportHelper.FillAutoStyle(sAutoStyleName, xGridProp); + } +} + +void SchXMLAxisContext::startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + // parse attributes + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(CHART, XML_DIMENSION): + { + SchXMLAxisDimension nEnumVal; + if( SvXMLUnitConverter::convertEnum( nEnumVal, aIter.toView(), aXMLAxisDimensionMap )) + m_aCurrentAxis.eDimension = nEnumVal; + } + break; + case XML_ELEMENT(CHART, XML_NAME): + m_aCurrentAxis.aName = aIter.toString(); + break; + case XML_ELEMENT(CHART, XML_AXIS_TYPE): + case XML_ELEMENT(CHART_EXT, XML_AXIS_TYPE): + sal_uInt16 nEnumVal; + if( SvXMLUnitConverter::convertEnum( nEnumVal, aIter.toView(), aXMLAxisTypeMap )) + { + m_nAxisType = nEnumVal; + m_bAxisTypeImported = true; + } + break; + case XML_ELEMENT(CHART, XML_STYLE_NAME): + m_aAutoStyleName = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + // check for number of axes with same dimension + m_aCurrentAxis.nAxisIndex = 0; + sal_Int32 nNumOfAxes = m_rAxes.size(); + for( sal_Int32 nCurrent = 0; nCurrent < nNumOfAxes; nCurrent++ ) + { + if( m_rAxes[ nCurrent ].eDimension == m_aCurrentAxis.eDimension ) + m_aCurrentAxis.nAxisIndex++; + } + CreateAxis(); +} +namespace +{ + +Reference< chart2::XAxis > lcl_getAxis( const Reference< frame::XModel >& xChartModel, + sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) +{ + Reference< chart2::XAxis > xAxis; + + try + { + Reference< chart2::XChartDocument > xChart2Document( xChartModel, uno::UNO_QUERY ); + if( xChart2Document.is() ) + { + Reference< chart2::XDiagram > xDiagram( xChart2Document->getFirstDiagram()); + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW ); + uno::Sequence< Reference< chart2::XCoordinateSystem > > + aCooSysSeq( xCooSysCnt->getCoordinateSystems()); + sal_Int32 nCooSysIndex = 0; + if( nCooSysIndex < aCooSysSeq.getLength() ) + { + Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIndex] ); + if( xCooSys.is() && nDimensionIndex < xCooSys->getDimension() ) + { + const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex); + if( nAxisIndex <= nMaxAxisIndex ) + xAxis = xCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex ); + } + } + } + } + catch( uno::Exception & ) + { + SAL_INFO("xmloff.chart", "Couldn't get axis" ); + } + + return xAxis; +} + +bool lcl_divideBy100( uno::Any& rDoubleAny ) +{ + bool bChanged = false; + double fValue=0.0; + if( (rDoubleAny>>=fValue) && (fValue!=0.0) ) + { + fValue/=100.0; + rDoubleAny <<= fValue; + bChanged = true; + } + return bChanged; +} + +bool lcl_AdaptWrongPercentScaleValues(chart2::ScaleData& rScaleData) +{ + bool bChanged = lcl_divideBy100( rScaleData.Minimum ); + bChanged = lcl_divideBy100( rScaleData.Maximum ) || bChanged; + bChanged = lcl_divideBy100( rScaleData.Origin ) || bChanged; + bChanged = lcl_divideBy100( rScaleData.IncrementData.Distance ) || bChanged; + return bChanged; +} + +}//end anonymous namespace + +void SchXMLAxisContext::CreateAxis() +{ + m_rAxes.push_back( m_aCurrentAxis ); + + Reference< beans::XPropertySet > xDiaProp( m_rImportHelper.GetChartDocument()->getDiagram(), uno::UNO_QUERY ); + if( !xDiaProp.is() ) + return; + OUString aPropName; + switch( m_aCurrentAxis.eDimension ) + { + case SCH_XML_AXIS_X: + if( m_aCurrentAxis.nAxisIndex == 0 ) + aPropName = "HasXAxis"; + else + aPropName = "HasSecondaryXAxis"; + break; + case SCH_XML_AXIS_Y: + if( m_aCurrentAxis.nAxisIndex == 0 ) + aPropName = "HasYAxis"; + else + aPropName = "HasSecondaryYAxis"; + break; + case SCH_XML_AXIS_Z: + if( m_aCurrentAxis.nAxisIndex == 0 ) + aPropName = "HasZAxis"; + break; + case SCH_XML_AXIS_UNDEF: + SAL_INFO("xmloff.chart", "Invalid axis" ); + break; + } + try + { + xDiaProp->setPropertyValue( aPropName, uno::Any(true) ); + } + catch( beans::UnknownPropertyException & ) + { + SAL_INFO("xmloff.chart", "Couldn't turn on axis" ); + } + if( m_aCurrentAxis.eDimension==SCH_XML_AXIS_Z ) + { + bool bSettingZAxisSucceeded = false; + try + { + xDiaProp->getPropertyValue( aPropName ) >>= bSettingZAxisSucceeded; + } + catch( beans::UnknownPropertyException & ) + { + SAL_INFO("xmloff.chart", "Couldn't turn on z axis" ); + } + if( !bSettingZAxisSucceeded ) + return; + } + + m_xAxisProps.set( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ), uno::UNO_QUERY ); + + if( m_bAddMissingXAxisForNetCharts && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y && m_aCurrentAxis.nAxisIndex==0 ) + { + try + { + xDiaProp->setPropertyValue("HasXAxis", uno::Any(true) ); + } + catch( beans::UnknownPropertyException & ) + { + SAL_INFO("xmloff.chart", "Couldn't turn on x axis" ); + } + } + + // set properties + if( !m_xAxisProps.is()) + return; + + uno::Any aTrueBool( uno::Any( true )); + uno::Any aFalseBool( uno::Any( false )); + + // #i109879# the line color is black as default, in the model it is a light gray + m_xAxisProps->setPropertyValue("LineColor", + uno::Any( COL_BLACK )); + + m_xAxisProps->setPropertyValue("DisplayLabels", aFalseBool ); + + // Compatibility option: starting from LibreOffice 5.1 the rotated + // layout is preferred to staggering for axis labels. + // So the import default value for having compatibility with ODF + // documents created with earlier LibreOffice versions is `true`. + if( GetImport().getGeneratorVersion() != SvXMLImport::ProductVersionUnknown ) + m_xAxisProps->setPropertyValue("TryStaggeringFirst", aTrueBool ); + + // #88077# AutoOrigin 'on' is default + m_xAxisProps->setPropertyValue("AutoOrigin", aTrueBool ); + + if( m_bAxisTypeImported ) + m_xAxisProps->setPropertyValue("AxisType", uno::Any(m_nAxisType) ); + + if( !m_aAutoStyleName.isEmpty()) + { + const SvXMLStylesContext* pStylesCtxt = m_rImportHelper.GetAutoStylesContext(); + if (pStylesCtxt) + { + SvXMLStyleContext* pStyle = const_cast<SvXMLStyleContext*>(pStylesCtxt->FindStyleChildContext(SchXMLImportHelper::GetChartFamilyID(), m_aAutoStyleName)); + + if (XMLPropStyleContext * pPropStyleContext = dynamic_cast<XMLPropStyleContext*>(pStyle)) + { + pPropStyleContext->FillPropertySet(m_xAxisProps); + + if( m_bAdaptWrongPercentScaleValues && m_aCurrentAxis.eDimension==SCH_XML_AXIS_Y ) + { + //set scale data of added x axis back to default + Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(), + m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) ); + if( xAxis.is() ) + { + chart2::ScaleData aScaleData( xAxis->getScaleData()); + if( lcl_AdaptWrongPercentScaleValues(aScaleData) ) + xAxis->setScaleData( aScaleData ); + } + } + + if( m_bAddMissingXAxisForNetCharts ) + { + //copy style from y axis to added x axis: + + Reference< chart::XAxisSupplier > xAxisSuppl( xDiaProp, uno::UNO_QUERY ); + if( xAxisSuppl.is() ) + { + Reference< beans::XPropertySet > xXAxisProp( xAxisSuppl->getAxis(0), uno::UNO_QUERY ); + pPropStyleContext->FillPropertySet(xXAxisProp); + } + + //set scale data of added x axis back to default + Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(), + 0 /*nDimensionIndex*/, 0 /*nAxisIndex*/ ) ); + if( xAxis.is() ) + { + chart2::ScaleData aScaleData; + aScaleData.AxisType = chart2::AxisType::CATEGORY; + aScaleData.Orientation = chart2::AxisOrientation_MATHEMATICAL; + xAxis->setScaleData( aScaleData ); + } + + //set line style of added x axis to invisible + Reference< beans::XPropertySet > xNewAxisProp( xAxis, uno::UNO_QUERY ); + if( xNewAxisProp.is() ) + { + xNewAxisProp->setPropertyValue("LineStyle" + , uno::Any(drawing::LineStyle_NONE)); + } + } + + if( m_bAdaptXAxisOrientationForOld2DBarCharts && m_aCurrentAxis.eDimension == SCH_XML_AXIS_X ) + { + bool bIs3DChart = false; + if( xDiaProp.is() && ( xDiaProp->getPropertyValue("Dim3D") >>= bIs3DChart ) + && !bIs3DChart ) + { + Reference< chart2::XChartDocument > xChart2Document( GetImport().GetModel(), uno::UNO_QUERY ); + if( xChart2Document.is() ) + { + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xChart2Document->getFirstDiagram(), uno::UNO_QUERY ); + if( xCooSysCnt.is() ) + { + uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() ); + if( aCooSysSeq.hasElements() ) + { + bool bSwapXandYAxis = false; + Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] ); + Reference< beans::XPropertySet > xCooSysProp( xCooSys, uno::UNO_QUERY ); + if( xCooSysProp.is() && ( xCooSysProp->getPropertyValue("SwapXAndYAxis") >>= bSwapXandYAxis ) + && bSwapXandYAxis ) + { + Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( 0, m_aCurrentAxis.nAxisIndex ); + if( xAxis.is() ) + { + chart2::ScaleData aScaleData = xAxis->getScaleData(); + aScaleData.Orientation = chart2::AxisOrientation_REVERSE; + xAxis->setScaleData( aScaleData ); + } + } + } + } + } + } + } + + m_rbAxisPositionAttributeImported = m_rbAxisPositionAttributeImported || SchXMLTools::getPropertyFromContext( + u"CrossoverPosition", pPropStyleContext, pStylesCtxt ).hasValue(); + } + } + } + + if (m_aCurrentAxis.eDimension != SCH_XML_AXIS_X) + return; + + Reference<chart2::XAxis> xAxis(lcl_getAxis(GetImport().GetModel(), m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex)); + if (!xAxis.is()) + return; + + chart2::ScaleData aScaleData(xAxis->getScaleData()); + bool bIs3DChart = false; + double fMajorOrigin = -1; + OUString sChartType = m_xDiagram->getDiagramType(); + if ((xDiaProp->getPropertyValue("Dim3D") >>= bIs3DChart) && bIs3DChart + && (sChartType == "com.sun.star.chart.BarDiagram" || sChartType == "com.sun.star.chart.StockDiagram")) + { + aScaleData.ShiftedCategoryPosition = true; + xAxis->setScaleData(aScaleData); + } + else if ((m_xAxisProps->getPropertyValue("MajorOrigin") >>= fMajorOrigin) + && (rtl::math::approxEqual(fMajorOrigin, 0.0) || rtl::math::approxEqual(fMajorOrigin, 0.5))) + { + aScaleData.ShiftedCategoryPosition = rtl::math::approxEqual(fMajorOrigin, 0.5); + xAxis->setScaleData(aScaleData); + } +} + +void SchXMLAxisContext::SetAxisTitle() +{ + if( m_aCurrentAxis.aTitle.isEmpty() ) + return; + + Reference< chart::XAxis > xAxis( lcl_getChartAxis( m_aCurrentAxis, m_xDiagram ) ); + if( !xAxis.is() ) + return; + + Reference< beans::XPropertySet > xTitleProp( xAxis->getAxisTitle() ); + if( xTitleProp.is() ) + { + try + { + xTitleProp->setPropertyValue("String", uno::Any(m_aCurrentAxis.aTitle) ); + } + catch( beans::UnknownPropertyException & ) + { + SAL_INFO("xmloff.chart", "Property String for Title not available" ); + } + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLAxisContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + switch( nElement ) + { + case XML_ELEMENT(CHART, XML_TITLE): + { + Reference< drawing::XShape > xTitleShape = getTitleShape(); + return new SchXMLTitleContext( m_rImportHelper, GetImport(), + m_aCurrentAxis.aTitle, + xTitleShape ); + } + break; + + case XML_ELEMENT(CHART, XML_CATEGORIES): + m_aCurrentAxis.bHasCategories = true; + return new SchXMLCategoriesContext( GetImport(), + m_rCategoriesAddress ); + break; + + case XML_ELEMENT(CHART, XML_DATE_SCALE): + case XML_ELEMENT(CHART_EXT, XML_DATE_SCALE): + m_bDateScaleImported = true; + return new DateScaleContext( GetImport(), m_xAxisProps ); + + case XML_ELEMENT(CHART, XML_GRID): + { + bool bIsMajor = true; // default value for class is "major" + OUString sAutoStyleName; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(CHART, XML_CLASS): + if( IsXMLToken( aIter, XML_MINOR ) ) + bIsMajor = false; + break; + case XML_ELEMENT(CHART, XML_STYLE_NAME): + sAutoStyleName = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + CreateGrid( sAutoStyleName, bIsMajor ); + + // don't create a context => use default context. grid elements are empty + } + break; + + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + break; + } + + return nullptr; +} + +void SchXMLAxisContext::endFastElement(sal_Int32 ) +{ + if( !m_bDateScaleImported && m_nAxisType==chart::ChartAxisType::AUTOMATIC ) + { + Reference< chart2::XAxis > xAxis( lcl_getAxis( GetImport().GetModel(), m_aCurrentAxis.eDimension, m_aCurrentAxis.nAxisIndex ) ); + if( xAxis.is() ) + { + chart2::ScaleData aScaleData( xAxis->getScaleData()); + aScaleData.AutoDateAxis = false;//different default for older documents + xAxis->setScaleData( aScaleData ); + } + } + + SetAxisTitle(); +} + +namespace +{ + +Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem >& rCooSys, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) +{ + Reference< chart2::XAxis > xAxis; + try + { + xAxis = rCooSys->getAxisByDimension( nDimensionIndex, nAxisIndex ); + } + catch( uno::Exception & ) + { + } + return xAxis; +} + +} // anonymous namespace + +void SchXMLAxisContext::CorrectAxisPositions( const Reference< chart2::XChartDocument >& xNewDoc, + std::u16string_view rChartTypeServiceName, + std::u16string_view rODFVersionOfFile, + bool bAxisPositionAttributeImported ) +{ + if( !(rODFVersionOfFile.empty() || rODFVersionOfFile == u"1.0" || rODFVersionOfFile == u"1.1" + || ( rODFVersionOfFile == u"1.2" && !bAxisPositionAttributeImported )) ) + return; + + try + { + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDoc->getFirstDiagram(), uno::UNO_QUERY_THROW ); + uno::Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems()); + if( aCooSysSeq.hasElements() ) + { + Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[0] ); + if( xCooSys.is() ) + { + Reference< chart2::XAxis > xMainXAxis = lcl_getAxis( xCooSys, 0, 0 ); + Reference< chart2::XAxis > xMainYAxis = lcl_getAxis( xCooSys, 1, 0 ); + //Reference< chart2::XAxis > xMajorZAxis = lcl_getAxis( xCooSys, 2, 0 ); + Reference< chart2::XAxis > xSecondaryXAxis = lcl_getAxis( xCooSys, 0, 1 ); + Reference< chart2::XAxis > xSecondaryYAxis = lcl_getAxis( xCooSys, 1, 1 ); + + Reference< beans::XPropertySet > xMainXAxisProp( xMainXAxis, uno::UNO_QUERY ); + Reference< beans::XPropertySet > xMainYAxisProp( xMainYAxis, uno::UNO_QUERY ); + Reference< beans::XPropertySet > xSecondaryXAxisProp( xSecondaryXAxis, uno::UNO_QUERY ); + Reference< beans::XPropertySet > xSecondaryYAxisProp( xSecondaryYAxis, uno::UNO_QUERY ); + + if( xMainXAxisProp.is() && xMainYAxisProp.is() ) + { + chart2::ScaleData aMainXScale = xMainXAxis->getScaleData(); + if( rChartTypeServiceName == u"com.sun.star.chart2.ScatterChartType" ) + { + xMainYAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_VALUE) ); + double fCrossoverValue = 0.0; + aMainXScale.Origin >>= fCrossoverValue; + xMainYAxisProp->setPropertyValue("CrossoverValue" + , uno::Any( fCrossoverValue ) ); + + if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE ) + { + xMainYAxisProp->setPropertyValue("LabelPosition" + , uno::Any( css::chart::ChartAxisLabelPosition_OUTSIDE_END) ); + xMainYAxisProp->setPropertyValue("MarkPosition" + , uno::Any( css::chart::ChartAxisMarkPosition_AT_LABELS) ); + if( xSecondaryYAxisProp.is() ) + xSecondaryYAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_START) ); + } + else + { + xMainYAxisProp->setPropertyValue("LabelPosition" + , uno::Any( css::chart::ChartAxisLabelPosition_OUTSIDE_START) ); + xMainYAxisProp->setPropertyValue("MarkPosition" + , uno::Any( css::chart::ChartAxisMarkPosition_AT_LABELS) ); + if( xSecondaryYAxisProp.is() ) + xSecondaryYAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_END) ); + } + } + else + { + if( aMainXScale.Orientation == chart2::AxisOrientation_REVERSE ) + { + xMainYAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_END) ); + if( xSecondaryYAxisProp.is() ) + xSecondaryYAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_START) ); + } + else + { + xMainYAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_START) ); + if( xSecondaryYAxisProp.is() ) + xSecondaryYAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_END) ); + } + } + + chart2::ScaleData aMainYScale = xMainYAxis->getScaleData(); + xMainXAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_VALUE) ); + double fCrossoverValue = 0.0; + aMainYScale.Origin >>= fCrossoverValue; + xMainXAxisProp->setPropertyValue("CrossoverValue" + , uno::Any( fCrossoverValue ) ); + + if( aMainYScale.Orientation == chart2::AxisOrientation_REVERSE ) + { + xMainXAxisProp->setPropertyValue("LabelPosition" + , uno::Any( css::chart::ChartAxisLabelPosition_OUTSIDE_END) ); + xMainXAxisProp->setPropertyValue("MarkPosition" + , uno::Any( css::chart::ChartAxisMarkPosition_AT_LABELS) ); + if( xSecondaryXAxisProp.is() ) + xSecondaryXAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_START) ); + } + else + { + xMainXAxisProp->setPropertyValue("LabelPosition" + , uno::Any( css::chart::ChartAxisLabelPosition_OUTSIDE_START) ); + xMainXAxisProp->setPropertyValue("MarkPosition" + , uno::Any( css::chart::ChartAxisMarkPosition_AT_LABELS) ); + if( xSecondaryXAxisProp.is() ) + xSecondaryXAxisProp->setPropertyValue("CrossoverPosition" + , uno::Any( css::chart::ChartAxisPosition_END) ); + } + } + } + } + } + catch( uno::Exception & ) + { + } +} + +SchXMLCategoriesContext::SchXMLCategoriesContext( + SvXMLImport& rImport, + OUString& rAddress ) : + SvXMLImportContext( rImport ), + mrAddress( rAddress ) +{ +} + +void SchXMLCategoriesContext::startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if( aIter.getToken() == XML_ELEMENT(TABLE, XML_CELL_RANGE_ADDRESS) ) + mrAddress = aIter.toString(); + else + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } +} + +DateScaleContext::DateScaleContext( + SvXMLImport& rImport, + const Reference< beans::XPropertySet >& rAxisProps ) : + SvXMLImportContext( rImport ), + m_xAxisProps( rAxisProps ) +{ +} + +namespace +{ +sal_Int32 lcl_getTimeUnit( const sax_fastparser::FastAttributeList::FastAttributeIter& rValue ) +{ + sal_Int32 nTimeUnit = css::chart::TimeUnit::DAY; + if( IsXMLToken( rValue, XML_DAYS ) ) + nTimeUnit = css::chart::TimeUnit::DAY; + else if( IsXMLToken( rValue, XML_MONTHS ) ) + nTimeUnit = css::chart::TimeUnit::MONTH; + else if( IsXMLToken( rValue, XML_YEARS ) ) + nTimeUnit = css::chart::TimeUnit::YEAR; + return nTimeUnit; +} + +} + +void DateScaleContext::startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + if( !m_xAxisProps.is() ) + return; + + // parse attributes + bool bSetNewIncrement=false; + chart::TimeIncrement aIncrement; + m_xAxisProps->getPropertyValue("TimeIncrement") >>= aIncrement; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch( aIter.getToken() ) + { + case XML_ELEMENT(CHART, XML_BASE_TIME_UNIT): + { + aIncrement.TimeResolution <<= lcl_getTimeUnit(aIter); + bSetNewIncrement = true; + } + break; + case XML_ELEMENT(CHART, XML_MAJOR_INTERVAL_VALUE): + { + chart::TimeInterval aInterval(1,0); + aIncrement.MajorTimeInterval >>= aInterval; + ::sax::Converter::convertNumber( aInterval.Number, aIter.toView() ); + aIncrement.MajorTimeInterval <<= aInterval; + bSetNewIncrement = true; + } + break; + case XML_ELEMENT(CHART, XML_MAJOR_INTERVAL_UNIT): + { + chart::TimeInterval aInterval(1,0); + aIncrement.MajorTimeInterval >>= aInterval; + aInterval.TimeUnit = lcl_getTimeUnit(aIter); + aIncrement.MajorTimeInterval <<= aInterval; + bSetNewIncrement = true; + } + break; + case XML_ELEMENT(CHART, XML_MINOR_INTERVAL_VALUE): + { + chart::TimeInterval aInterval(1,0); + aIncrement.MinorTimeInterval >>= aInterval; + ::sax::Converter::convertNumber( aInterval.Number, aIter.toView() ); + aIncrement.MinorTimeInterval <<= aInterval; + bSetNewIncrement = true; + } + break; + case XML_ELEMENT(CHART, XML_MINOR_INTERVAL_UNIT): + { + chart::TimeInterval aInterval(1,0); + aIncrement.MinorTimeInterval >>= aInterval; + aInterval.TimeUnit = lcl_getTimeUnit(aIter); + aIncrement.MinorTimeInterval <<= aInterval; + bSetNewIncrement = true; + } + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + if( bSetNewIncrement ) + m_xAxisProps->setPropertyValue("TimeIncrement", uno::Any( aIncrement ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLAxisContext.hxx b/xmloff/source/chart/SchXMLAxisContext.hxx new file mode 100644 index 000000000..f128edeab --- /dev/null +++ b/xmloff/source/chart/SchXMLAxisContext.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/SchXMLImportHelper.hxx> +#include <xmloff/xmlictxt.hxx> + +#include "transporttypes.hxx" + +#include <com/sun/star/chart/XDiagram.hpp> +#include <com/sun/star/xml/sax/XAttributeList.hpp> + +class SchXMLAxisContext : public SvXMLImportContext +{ +public: + SchXMLAxisContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + css::uno::Reference< css::chart::XDiagram > const & xDiagram, + std::vector< SchXMLAxis >& aAxes, + OUString& rCategoriesAddress, + bool bAddMissingXAxisForNetCharts, + bool bAdaptWrongPercentScaleValues, + bool bAdaptXAxisOrientationForOld2DBarCharts, + bool& rbAxisPositionAttributeImported ); + virtual ~SchXMLAxisContext() override; + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + static void CorrectAxisPositions( const css::uno::Reference< css::chart2::XChartDocument >& xNewDoc, + std::u16string_view rChartTypeServiceName, + std::u16string_view rODFVersionOfFile, + bool bAxisPositionAttributeImported ); + +private: + SchXMLImportHelper& m_rImportHelper; + css::uno::Reference< css::chart::XDiagram > m_xDiagram; + SchXMLAxis m_aCurrentAxis; + std::vector< SchXMLAxis >& m_rAxes; + css::uno::Reference< css::beans::XPropertySet > m_xAxisProps; + OUString m_aAutoStyleName; + OUString& m_rCategoriesAddress; + sal_Int32 m_nAxisType;//css::chart::ChartAxisType + bool m_bAxisTypeImported; + bool m_bDateScaleImported; + bool m_bAddMissingXAxisForNetCharts; //to correct errors from older versions + bool m_bAdaptWrongPercentScaleValues; //to correct errors from older versions + bool m_bAdaptXAxisOrientationForOld2DBarCharts; //to correct different behaviour from older versions + bool& m_rbAxisPositionAttributeImported; + + css::uno::Reference< css::drawing::XShape > getTitleShape() const; + void CreateGrid( const OUString& sAutoStyleName, bool bIsMajor ); + void CreateAxis(); + void SetAxisTitle(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLCalculationSettingsContext.cxx b/xmloff/source/chart/SchXMLCalculationSettingsContext.cxx new file mode 100644 index 000000000..fbe54c96d --- /dev/null +++ b/xmloff/source/chart/SchXMLCalculationSettingsContext.cxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLCalculationSettingsContext.hxx" +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <sal/log.hxx> +#include <sax/tools/converter.hxx> + +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +SchXMLCalculationSettingsContext::SchXMLCalculationSettingsContext( SvXMLImport& rImport, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +: SvXMLImportContext ( rImport ) +{ + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if ( aIter.getToken() == XML_ELEMENT(TABLE, XML_DATE_VALUE) ) + { + util::DateTime aNullDate; + if (::sax::Converter::parseDateTime(aNullDate, aIter.toView())) + m_aNullDate <<= aNullDate; + else + SAL_WARN("xmloff", "SchXMLCalculationSettingsContext: broken DateTime '" << aIter.toView() << "'"); + } + else + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLCalculationSettingsContext::createFastChildContext( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + return new SchXMLCalculationSettingsContext(GetImport(),xAttrList); +} + +void SchXMLCalculationSettingsContext::endFastElement(sal_Int32 ) +{ + if ( m_aNullDate.hasValue() ) + { + Reference < XPropertySet > xPropSet ( GetImport().GetModel(), UNO_QUERY ); + xPropSet->setPropertyValue ( "NullDate", m_aNullDate ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLCalculationSettingsContext.hxx b/xmloff/source/chart/SchXMLCalculationSettingsContext.hxx new file mode 100644 index 000000000..b10f1545d --- /dev/null +++ b/xmloff/source/chart/SchXMLCalculationSettingsContext.hxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <xmloff/xmlictxt.hxx> + +class SchXMLCalculationSettingsContext : public SvXMLImportContext +{ + css::uno::Any m_aNullDate; +public: + SchXMLCalculationSettingsContext( SvXMLImport& rImport, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLChartContext.cxx b/xmloff/source/chart/SchXMLChartContext.cxx new file mode 100644 index 000000000..23ea2f780 --- /dev/null +++ b/xmloff/source/chart/SchXMLChartContext.cxx @@ -0,0 +1,1235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLChartContext.hxx" +#include <SchXMLImport.hxx> +#include "SchXMLLegendContext.hxx" +#include "SchXMLPlotAreaContext.hxx" +#include "SchXMLParagraphContext.hxx" +#include "SchXMLTableContext.hxx" +#include "SchXMLSeries2Context.hxx" +#include "SchXMLTools.hxx" +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/mediadescriptor.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmlstyle.hxx> +#include <xmloff/SchXMLSeriesHelper.hxx> + +#include <vector> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart/XDiagram.hpp> +#include <com/sun/star/xml/sax/XAttributeList.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/drawing/XDrawPage.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XVisualObject.hpp> + +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/data/XDataSink.hpp> +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XTitled.hpp> + +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> + +using namespace com::sun::star; +using namespace ::xmloff::token; +using com::sun::star::uno::Reference; +using namespace ::SchXMLTools; + +namespace +{ + +void lcl_setRoleAtLabeledSequence( + const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq, + const OUString &rRole ) +{ + // set role of sequence + uno::Reference< chart2::data::XDataSequence > xValues( xLSeq->getValues()); + if( xValues.is()) + { + uno::Reference< beans::XPropertySet > xProp( xValues, uno::UNO_QUERY ); + if( xProp.is()) + xProp->setPropertyValue("Role", uno::Any( rRole )); + } +} + +void lcl_MoveDataToCandleStickSeries( + const uno::Reference< chart2::data::XDataSource > & xDataSource, + const uno::Reference< chart2::XDataSeries > & xDestination, + const OUString & rRole ) +{ + try + { + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledSeq( + xDataSource->getDataSequences()); + if( aLabeledSeq.hasElements()) + { + lcl_setRoleAtLabeledSequence( aLabeledSeq[0], rRole ); + + // add to data series + uno::Reference< chart2::data::XDataSource > xSource( xDestination, uno::UNO_QUERY_THROW ); + // @todo: realloc only once outside this function + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aData( xSource->getDataSequences()); + aData.realloc( aData.getLength() + 1); + aData.getArray()[ aData.getLength() - 1 ] = aLabeledSeq[0]; + uno::Reference< chart2::data::XDataSink > xSink( xDestination, uno::UNO_QUERY_THROW ); + xSink->setData( aData ); + } + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception caught while moving data to candlestick series" ); + } +} + +void lcl_setRoleAtFirstSequence( + const uno::Reference< chart2::XDataSeries > & xSeries, + const OUString & rRole ) +{ + uno::Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY ); + if( xSource.is()) + { + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSeq( xSource->getDataSequences()); + if( aSeq.hasElements()) + lcl_setRoleAtLabeledSequence( aSeq[0], rRole ); + } +} + +void lcl_removeEmptyChartTypeGroups( const uno::Reference< chart2::XChartDocument > & xDoc ) +{ + if( ! xDoc.is()) + return; + + uno::Reference< chart2::XDiagram > xDia( xDoc->getFirstDiagram()); + if( ! xDia.is()) + return; + + try + { + // count all charttype groups to be able to leave at least one + sal_Int32 nRemainingGroups = 0; + uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDia, uno::UNO_QUERY_THROW ); + const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > + aCooSysSeq( xCooSysCnt->getCoordinateSystems()); + for( auto const & i : aCooSysSeq ) + { + uno::Reference< chart2::XChartTypeContainer > xCTCnt( i, uno::UNO_QUERY_THROW ); + nRemainingGroups += xCTCnt->getChartTypes().getLength(); + } + + // delete all empty groups, but leave at least group (empty or not) + for( sal_Int32 nI = aCooSysSeq.getLength(); nI-- && (nRemainingGroups > 1); ) + { + uno::Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nI], uno::UNO_QUERY_THROW ); + uno::Sequence< uno::Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes()); + for( sal_Int32 nJ=aCTSeq.getLength(); nJ-- && (nRemainingGroups > 1); ) + { + uno::Reference< chart2::XDataSeriesContainer > xDSCnt( aCTSeq[nJ], uno::UNO_QUERY_THROW ); + if( !xDSCnt->getDataSeries().hasElements() ) + { + // note: iterator stays valid as we have a local sequence + xCTCnt->removeChartType( aCTSeq[nJ] ); + --nRemainingGroups; + } + } + } + } + catch(const uno::Exception&) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught while removing empty chart types"); + } +} + +uno::Sequence< sal_Int32 > lcl_getNumberSequenceFromString( const OUString& rStr, bool bAddOneToEachOldIndex ) +{ + const sal_Unicode aSpace( ' ' ); + + // count number of entries + ::std::vector< sal_Int32 > aVec; + sal_Int32 nLastPos = 0; + sal_Int32 nPos = 0; + while( nPos != -1 ) + { + nPos = rStr.indexOf( aSpace, nLastPos ); + if( nPos > nLastPos ) + { + aVec.push_back( o3tl::toInt32(rStr.subView( nLastPos, (nPos - nLastPos) )) ); + } + if( nPos != -1 ) + nLastPos = nPos + 1; + } + // last entry + if( nLastPos != 0 && + rStr.getLength() > nLastPos ) + { + aVec.push_back( o3tl::toInt32(rStr.subView( nLastPos )) ); + } + + const sal_Int32 nVecSize = aVec.size(); + uno::Sequence< sal_Int32 > aSeq( nVecSize ); + + if(!bAddOneToEachOldIndex) + { + sal_Int32* pSeqArr = aSeq.getArray(); + for( nPos = 0; nPos < nVecSize; ++nPos ) + { + pSeqArr[ nPos ] = aVec[ nPos ]; + } + } + else if( bAddOneToEachOldIndex ) + { + aSeq.realloc( nVecSize+1 ); + auto pSeqArr = aSeq.getArray(); + pSeqArr[0]=0; + + for( nPos = 0; nPos < nVecSize; ++nPos ) + { + pSeqArr[ nPos+1 ] = aVec[ nPos ]+1; + } + } + + return aSeq; +} + +} // anonymous namespace + +SchXMLChartContext::SchXMLChartContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + m_bHasRangeAtPlotArea( false ), + m_bHasTableElement( false ), + mbAllRangeAddressesAvailable( true ), + mbColHasLabels( false ), + mbRowHasLabels( false ), + meDataRowSource( chart::ChartDataRowSource_COLUMNS ), + mbIsStockChart( false ) +{ +} + +SchXMLChartContext::~SchXMLChartContext() +{} + +static bool lcl_hasServiceName(Reference<lang::XMultiServiceFactory> const & xFactory, OUString const & rServiceName) +{ + const uno::Sequence<OUString> aServiceNames(xFactory->getAvailableServiceNames()); + + return std::find(aServiceNames.begin(), aServiceNames.end(), rServiceName) != aServiceNames.end(); +} + +void setDataProvider(uno::Reference<chart2::XChartDocument> const & xChartDoc, OUString const & sDataPilotSource) +{ + if (!xChartDoc.is()) + return; + + try + { + uno::Reference<container::XChild> xChild(xChartDoc, uno::UNO_QUERY); + uno::Reference<chart2::data::XDataReceiver> xDataReceiver(xChartDoc, uno::UNO_QUERY); + if (xChild.is() && xDataReceiver.is()) + { + bool bHasOwnData = true; + + Reference<lang::XMultiServiceFactory> xFact(xChild->getParent(), uno::UNO_QUERY); + if (xFact.is()) + { + if (!xChartDoc->getDataProvider().is()) + { + bool bHasDataPilotSource = !sDataPilotSource.isEmpty(); + OUString aDataProviderServiceName("com.sun.star.chart2.data.DataProvider"); + if (bHasDataPilotSource) + aDataProviderServiceName = "com.sun.star.chart2.data.PivotTableDataProvider"; + + if (lcl_hasServiceName(xFact, aDataProviderServiceName)) + { + Reference<chart2::data::XDataProvider> xProvider(xFact->createInstance(aDataProviderServiceName), uno::UNO_QUERY); + + if (xProvider.is()) + { + if (bHasDataPilotSource) + { + Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xProvider, uno::UNO_QUERY); + xPivotTableDataProvider->setPivotTableName(sDataPilotSource); + xDataReceiver->attachDataProvider(xProvider); + bHasOwnData = !xPivotTableDataProvider->hasPivotTable(); + } + else + { + xDataReceiver->attachDataProvider(xProvider); + bHasOwnData = false; + } + } + } + } + else + bHasOwnData = false; + } + // else we have no parent => we have our own data + + if (bHasOwnData && ! xChartDoc->hasInternalDataProvider()) + xChartDoc->createInternalDataProvider(false); + } + } + catch (const uno::Exception &) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "SchXMLChartContext::StartElement()"); + } +} + +void SchXMLChartContext::startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + // parse attributes + + uno::Reference< embed::XVisualObject > xVisualObject( mrImportHelper.GetChartDocument(), uno::UNO_QUERY); + SAL_WARN_IF(!xVisualObject.is(), "xmloff.chart", "need xVisualObject for page size"); + if( xVisualObject.is() ) + maChartSize = xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); //#i103460# take the size given from the parent frame as default + + OUString sAutoStyleName; + OUString aOldChartTypeName; + bool bHasAddin = false; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch( aIter.getToken() ) + { + case XML_ELEMENT(LO_EXT, XML_DATA_PILOT_SOURCE): + msDataPilotSource = aIter.toString(); + break; + case XML_ELEMENT(XLINK, XML_HREF): + m_aXLinkHRefAttributeToIndicateDataProvider = aIter.toString(); + break; + case XML_ELEMENT(CHART, XML_CLASS): + { + OUString aValue = aIter.toString(); + OUString sClassName; + sal_uInt16 nClassPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrValueQName( + aValue, &sClassName ); + if( XML_NAMESPACE_CHART == nClassPrefix ) + { + SchXMLChartTypeEnum eChartTypeEnum = SchXMLTools::GetChartTypeEnum( sClassName ); + if( eChartTypeEnum != XML_CHART_CLASS_UNKNOWN ) + { + aOldChartTypeName = SchXMLTools::GetChartTypeByClassName( sClassName, true /* bUseOldNames */ ); + maChartTypeServiceName = SchXMLTools::GetChartTypeByClassName( sClassName, false /* bUseOldNames */ ); + switch( eChartTypeEnum ) + { + case XML_CHART_CLASS_STOCK: + mbIsStockChart = true; + break; + default: + break; + } + } + } + else if( XML_NAMESPACE_OOO == nClassPrefix ) + { + // service is taken from add-in-name attribute + bHasAddin = true; + + aOldChartTypeName = sClassName; + maChartTypeServiceName = sClassName; + } + } + break; + + case XML_ELEMENT(SVG, XML_WIDTH): + case XML_ELEMENT(SVG_COMPAT, XML_WIDTH): + GetImport().GetMM100UnitConverter().convertMeasureToCore( + maChartSize.Width, aIter.toView() ); + break; + + case XML_ELEMENT(SVG, XML_HEIGHT): + case XML_ELEMENT(SVG_COMPAT, XML_HEIGHT): + GetImport().GetMM100UnitConverter().convertMeasureToCore( + maChartSize.Height, aIter.toView() ); + break; + + case XML_ELEMENT(CHART, XML_STYLE_NAME): + sAutoStyleName = aIter.toString(); + break; + + case XML_ELEMENT(CHART, XML_COLUMN_MAPPING): + msColTrans = aIter.toString(); + break; + case XML_ELEMENT(CHART, XML_ROW_MAPPING): + msRowTrans = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + uno::Reference<chart::XChartDocument> xDoc = mrImportHelper.GetChartDocument(); + uno::Reference<chart2::XChartDocument> xNewDoc(xDoc, uno::UNO_QUERY); + + setDataProvider(xNewDoc, msDataPilotSource); + + if( aOldChartTypeName.isEmpty() ) + { + SAL_WARN("xmloff.chart", "need a charttype to create a diagram" ); + //set a fallback value: + const OUString& aChartClass_Bar( GetXMLToken(XML_BAR ) ); + aOldChartTypeName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, true /* bUseOldNames */ ); + maChartTypeServiceName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, false /* bUseOldNames */ ); + } + + // Set the size of the draw page. + if( xVisualObject.is() ) + xVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, maChartSize ); + + InitChart( aOldChartTypeName); + + if( bHasAddin ) + { + //correct charttype service name when having an addin + //and don't refresh addin during load + uno::Reference< beans::XPropertySet > xDocProp( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ); + if( xDocProp.is() ) + { + try + { + xDocProp->getPropertyValue("BaseDiagram") >>= aOldChartTypeName; + maChartTypeServiceName = SchXMLTools::GetNewChartTypeName( aOldChartTypeName ); + xDocProp->setPropertyValue("RefreshAddInAllowed", uno::Any( false) ); + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::StartElement" ); + } + } + } + + // set auto-styles for Area + uno::Reference<beans::XPropertySet> xProp = mrImportHelper.GetChartDocument()->getArea(); + mrImportHelper.FillAutoStyle(sAutoStyleName, xProp); +} + +namespace +{ + +struct NewDonutSeries +{ + css::uno::Reference< css::chart2::XDataSeries > m_xSeries; + OUString msStyleName; + sal_Int32 mnAttachedAxis; + + ::std::vector< OUString > m_aSeriesStyles; + ::std::vector< OUString > m_aPointStyles; + + NewDonutSeries( const css::uno::Reference< css::chart2::XDataSeries >& xSeries, sal_Int32 nPointCount ) + : m_xSeries( xSeries ) + , mnAttachedAxis( 1 ) + { + m_aPointStyles.resize(nPointCount); + m_aSeriesStyles.resize(nPointCount); + } + + void setSeriesStyleNameToPoint( const OUString& rStyleName, sal_Int32 nPointIndex ) + { + SAL_WARN_IF(nPointIndex >= static_cast<sal_Int32>(m_aSeriesStyles.size()), "xmloff.chart", "donut point <-> series count mismatch"); + if( nPointIndex < static_cast<sal_Int32>(m_aSeriesStyles.size()) ) + m_aSeriesStyles[nPointIndex]=rStyleName; + } + + void setPointStyleNameToPoint( const OUString& rStyleName, sal_Int32 nPointIndex ) + { + SAL_WARN_IF(nPointIndex >= static_cast<sal_Int32>(m_aPointStyles.size()), "xmloff.chart", "donut point <-> series count mismatch"); + if( nPointIndex < static_cast<sal_Int32>(m_aPointStyles.size()) ) + m_aPointStyles[nPointIndex]=rStyleName; + } + + ::std::vector< DataRowPointStyle > creatStyleVector() + { + ::std::vector< DataRowPointStyle > aRet; + + DataRowPointStyle aSeriesStyle( DataRowPointStyle::DATA_SERIES + , m_xSeries, -1, 1, msStyleName, mnAttachedAxis ); + aRet.push_back( aSeriesStyle ); + + sal_Int32 nPointIndex=0; + for (auto const& pointStyle : m_aPointStyles) + { + DataRowPointStyle aPointStyle( DataRowPointStyle::DATA_POINT + , m_xSeries, nPointIndex, 1, pointStyle, mnAttachedAxis ); + if( nPointIndex < static_cast<sal_Int32>(m_aSeriesStyles.size()) ) + { + aPointStyle.msSeriesStyleNameForDonuts = m_aSeriesStyles[nPointIndex]; + } + if( !aPointStyle.msSeriesStyleNameForDonuts.isEmpty() + || !aPointStyle.msStyleName.isEmpty() ) + aRet.push_back( aPointStyle ); + ++nPointIndex; + } + + return aRet; + } +}; + +void lcl_swapPointAndSeriesStylesForDonutCharts( ::std::vector< DataRowPointStyle >& rStyleVector + , ::std::map< css::uno::Reference< css::chart2::XDataSeries> , sal_Int32 >&& aSeriesMap ) +{ + //detect old series count + //and add old series to aSeriesMap + sal_Int32 nOldSeriesCount = 0; + { + sal_Int32 nMaxOldSeriesIndex = 0; + sal_Int32 nOldSeriesIndex = 0; + for (auto const& style : rStyleVector) + { + DataRowPointStyle aStyle(style); + if(aStyle.meType == DataRowPointStyle::DATA_SERIES && + aStyle.m_xSeries.is() ) + { + nMaxOldSeriesIndex = nOldSeriesIndex; + + if( aSeriesMap.end() == aSeriesMap.find(aStyle.m_xSeries) ) + aSeriesMap[aStyle.m_xSeries] = nOldSeriesIndex; + + nOldSeriesIndex++; + } + } + nOldSeriesCount = nMaxOldSeriesIndex+1; + } + + //initialize new series styles + ::std::map< Reference< chart2::XDataSeries >, sal_Int32 >::const_iterator aSeriesMapEnd( aSeriesMap.end() ); + + //sort by index + ::std::vector< NewDonutSeries > aNewSeriesVector; + { + ::std::map< sal_Int32, Reference< chart2::XDataSeries > > aIndexSeriesMap; + for (auto const& series : aSeriesMap) + aIndexSeriesMap[series.second] = series.first; + + for (auto const& indexSeries : aIndexSeriesMap) + aNewSeriesVector.emplace_back(indexSeries.second,nOldSeriesCount ); + } + + //overwrite attached axis information according to old series styles + for (auto const& style : rStyleVector) + { + DataRowPointStyle aStyle(style); + if(aStyle.meType == DataRowPointStyle::DATA_SERIES ) + { + auto aSeriesMapIt = aSeriesMap.find( aStyle.m_xSeries ); + if( aSeriesMapIt != aSeriesMapEnd && aSeriesMapIt->second < static_cast<sal_Int32>(aNewSeriesVector.size()) ) + aNewSeriesVector[aSeriesMapIt->second].mnAttachedAxis = aStyle.mnAttachedAxis; + } + } + + //overwrite new series style names with old series style name information + for (auto const& style : rStyleVector) + { + DataRowPointStyle aStyle(style); + if( aStyle.meType == DataRowPointStyle::DATA_SERIES ) + { + auto aSeriesMapIt = aSeriesMap.find(aStyle.m_xSeries); + if( aSeriesMapEnd != aSeriesMapIt ) + { + sal_Int32 nNewPointIndex = aSeriesMapIt->second; + + for (auto & newSeries : aNewSeriesVector) + newSeries.setSeriesStyleNameToPoint( aStyle.msStyleName, nNewPointIndex ); + } + } + } + + //overwrite new series style names with point style name information + for (auto const& style : rStyleVector) + { + DataRowPointStyle aStyle(style); + if( aStyle.meType == DataRowPointStyle::DATA_POINT ) + { + auto aSeriesMapIt = aSeriesMap.find(aStyle.m_xSeries); + if( aSeriesMapEnd != aSeriesMapIt ) + { + sal_Int32 nNewPointIndex = aSeriesMapIt->second; + sal_Int32 nNewSeriesIndex = aStyle.m_nPointIndex; + sal_Int32 nRepeatCount = aStyle.m_nPointRepeat; + + while( nRepeatCount && (nNewSeriesIndex>=0) && (o3tl::make_unsigned(nNewSeriesIndex)< aNewSeriesVector.size() ) ) + { + NewDonutSeries& rNewSeries( aNewSeriesVector[nNewSeriesIndex] ); + rNewSeries.setPointStyleNameToPoint( aStyle.msStyleName, nNewPointIndex ); + + nRepeatCount--; + nNewSeriesIndex++; + } + } + } + } + + //put information from aNewSeriesVector to output parameter rStyleVector + rStyleVector.clear(); + + for (auto & newSeries : aNewSeriesVector) + { + ::std::vector< DataRowPointStyle > aVector( newSeries.creatStyleVector() ); + rStyleVector.insert(rStyleVector.end(),aVector.begin(),aVector.end()); + } +} + +bool lcl_SpecialHandlingForDonutChartNeeded( + std::u16string_view rServiceName, + const SvXMLImport & rImport ) +{ + bool bResult = false; + if( rServiceName == u"com.sun.star.chart2.DonutChartType" ) + { + bResult = SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( rImport.GetModel() ); + } + return bResult; +} + +} // anonymous namespace + +static void lcl_ApplyDataFromRectangularRangeToDiagram( + const uno::Reference< chart2::XChartDocument >& xNewDoc + , const OUString& rRectangularRange + , css::chart::ChartDataRowSource eDataRowSource + , bool bRowHasLabels, bool bColHasLabels + , bool bSwitchOnLabelsAndCategoriesForOwnData + , const OUString& sColTrans + , const OUString& sRowTrans ) +{ + if( !xNewDoc.is() ) + return; + + uno::Reference< chart2::XDiagram > xNewDia( xNewDoc->getFirstDiagram()); + uno::Reference< chart2::data::XDataProvider > xDataProvider( xNewDoc->getDataProvider() ); + if( !xNewDia.is() || !xDataProvider.is() ) + return; + + bool bFirstCellAsLabel = + (eDataRowSource==chart::ChartDataRowSource_COLUMNS)? bRowHasLabels : bColHasLabels; + bool bHasCateories = + (eDataRowSource==chart::ChartDataRowSource_COLUMNS)? bColHasLabels : bRowHasLabels; + + if( bSwitchOnLabelsAndCategoriesForOwnData ) + { + bFirstCellAsLabel = true; + bHasCateories = true; + } + + uno::Sequence< beans::PropertyValue > aArgs{ + beans::PropertyValue( + "CellRangeRepresentation", + -1, uno::Any( rRectangularRange ), + beans::PropertyState_DIRECT_VALUE ), + beans::PropertyValue( + "DataRowSource", + -1, uno::Any( eDataRowSource ), + beans::PropertyState_DIRECT_VALUE ), + beans::PropertyValue( + "FirstCellAsLabel", + -1, uno::Any( bFirstCellAsLabel ), + beans::PropertyState_DIRECT_VALUE ) + }; + + if( !sColTrans.isEmpty() || !sRowTrans.isEmpty() ) + { + aArgs.realloc( aArgs.getLength() + 1 ); + aArgs.getArray()[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue( + "SequenceMapping", + -1, uno::Any( !sColTrans.isEmpty() + ? lcl_getNumberSequenceFromString( sColTrans, bHasCateories && !xNewDoc->hasInternalDataProvider() ) + : lcl_getNumberSequenceFromString( sRowTrans, bHasCateories && !xNewDoc->hasInternalDataProvider() ) ), + beans::PropertyState_DIRECT_VALUE ); + } + + //work around wrong writer ranges ( see Issue 58464 ) + { + OUString aChartOleObjectName; + if( xNewDoc.is() ) + { + utl::MediaDescriptor aMediaDescriptor( xNewDoc->getArgs() ); + + utl::MediaDescriptor::const_iterator aIt( + aMediaDescriptor.find( OUString( "HierarchicalDocumentName" ))); + if( aIt != aMediaDescriptor.end() ) + { + aChartOleObjectName = (*aIt).second.get< OUString >(); + } + } + if( !aChartOleObjectName.isEmpty() ) + { + aArgs.realloc( aArgs.getLength() + 1 ); + aArgs.getArray()[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue( + "ChartOleObjectName", + -1, uno::Any( aChartOleObjectName ), + beans::PropertyState_DIRECT_VALUE ); + } + } + + uno::Reference< chart2::data::XDataSource > xDataSource( + xDataProvider->createDataSource( aArgs )); + + aArgs.realloc( aArgs.getLength() + 2 ); + auto pArgs = aArgs.getArray(); + pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 2 ] = beans::PropertyValue( + "HasCategories", + -1, uno::Any( bHasCateories ), + beans::PropertyState_DIRECT_VALUE ); + pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue( + "UseCategoriesAsX", + -1, uno::Any( false ),//categories in ODF files are not to be used as x values (independent from what is offered in our ui) + beans::PropertyState_DIRECT_VALUE ); + + xNewDia->setDiagramData( xDataSource, aArgs ); +} + +void SchXMLChartContext::endFastElement(sal_Int32 ) +{ + uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument(); + uno::Reference< beans::XPropertySet > xProp( xDoc, uno::UNO_QUERY ); + uno::Reference< chart2::XChartDocument > xNewDoc( xDoc, uno::UNO_QUERY ); + + if( xProp.is()) + { + if( !maMainTitle.isEmpty()) + { + uno::Reference< beans::XPropertySet > xTitleProp( xDoc->getTitle(), uno::UNO_QUERY ); + if( xTitleProp.is()) + { + try + { + xTitleProp->setPropertyValue("String", uno::Any(maMainTitle) ); + } + catch(const beans::UnknownPropertyException&) + { + SAL_WARN("xmloff.chart", "Property String for Title not available" ); + } + } + } + if( !maSubTitle.isEmpty()) + { + uno::Reference< beans::XPropertySet > xTitleProp( xDoc->getSubTitle(), uno::UNO_QUERY ); + if( xTitleProp.is()) + { + try + { + xTitleProp->setPropertyValue("String", uno::Any(maSubTitle) ); + } + catch(const beans::UnknownPropertyException&) + { + SAL_WARN("xmloff.chart", "Property String for Title not available" ); + } + } + } + } + + // cleanup: remove empty chart type groups + lcl_removeEmptyChartTypeGroups( xNewDoc ); + + // set stack mode before a potential chart type detection (in case we have a rectangular range) + uno::Reference< chart::XDiagram > xDiagram( xDoc->getDiagram() ); + uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY ); + if( xDiaProp.is()) + { + if( maSeriesDefaultsAndStyles.maStackedDefault.hasValue()) + xDiaProp->setPropertyValue("Stacked",maSeriesDefaultsAndStyles.maStackedDefault); + if( maSeriesDefaultsAndStyles.maPercentDefault.hasValue()) + xDiaProp->setPropertyValue("Percent",maSeriesDefaultsAndStyles.maPercentDefault); + if( maSeriesDefaultsAndStyles.maDeepDefault.hasValue()) + xDiaProp->setPropertyValue("Deep",maSeriesDefaultsAndStyles.maDeepDefault); + if( maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault.hasValue()) + xDiaProp->setPropertyValue("StackedBarsConnected",maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault); + } + + //the OOo 2.0 implementation and older has a bug with donuts + bool bSpecialHandlingForDonutChart = lcl_SpecialHandlingForDonutChartNeeded( + maChartTypeServiceName, GetImport()); + + // apply data + if(!xNewDoc.is()) + return; + + bool bHasOwnData = false; + if( m_aXLinkHRefAttributeToIndicateDataProvider == "." ) //data comes from the chart itself + bHasOwnData = true; + else if( m_aXLinkHRefAttributeToIndicateDataProvider == ".." ) //data comes from the parent application + bHasOwnData = false; + else if( !m_aXLinkHRefAttributeToIndicateDataProvider.isEmpty() ) //not supported so far to get the data by sibling objects -> fall back to chart itself if data are available + bHasOwnData = m_bHasTableElement; + else + bHasOwnData = !m_bHasRangeAtPlotArea; + + if( xNewDoc->hasInternalDataProvider()) + { + if( !m_bHasTableElement && m_aXLinkHRefAttributeToIndicateDataProvider != "." ) + { + //#i103147# ODF, workaround broken files with a missing table:cell-range-address at the plot-area + bool bSwitchSuccessful = SchXMLTools::switchBackToDataProviderFromParent( xNewDoc, maLSequencesPerIndex ); + bHasOwnData = !bSwitchSuccessful; + } + else + bHasOwnData = true;//e.g. in case of copy->paste from calc to impress + } + else if( bHasOwnData ) + { + xNewDoc->createInternalDataProvider( false /* bCloneExistingData */ ); + } + if( bHasOwnData ) + msChartAddress = "all"; + + bool bSwitchRangesFromOuterToInternalIfNecessary = false; + if( !bHasOwnData && mbAllRangeAddressesAvailable ) + { + // special handling for stock chart (merge series together) + if( mbIsStockChart ) + MergeSeriesForStockChart(); + } + else if( !msChartAddress.isEmpty() ) + { + //own data or only rectangular range available + + if( xNewDoc->hasInternalDataProvider() ) + SchXMLTableHelper::applyTableToInternalDataProvider( maTable, xNewDoc ); + + bool bOlderThan2_3 = SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( xNewDoc ); + bool bOldFileWithOwnDataFromRows = (bOlderThan2_3 && bHasOwnData && (meDataRowSource==chart::ChartDataRowSource_ROWS)); // in this case there are range addresses that are simply wrong. + + if( mbAllRangeAddressesAvailable && !bSpecialHandlingForDonutChart && !mbIsStockChart && + !bOldFileWithOwnDataFromRows ) + { + //bHasOwnData is true in this case! + //e.g. for normal files with own data or also in case of copy paste scenario (e.g. calc to impress) + bSwitchRangesFromOuterToInternalIfNecessary = true; + } + else + { + //apply data from rectangular range + + // create datasource from data provider with rectangular range parameters and change the diagram setDiagramData + try + { + if( bOlderThan2_3 && xDiaProp.is() )//for older charts the hidden cells were removed by calc on the fly + xDiaProp->setPropertyValue("IncludeHiddenCells",uno::Any(false)); + + // note: mbRowHasLabels means the first row contains labels, that means we have "column-descriptions", + // (analogously mbColHasLabels means we have "row-descriptions") + lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans ); + } + catch(const uno::Exception&) + { + //try to fallback to internal data + TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram try to fallback to internal data" ); + if(!bHasOwnData) + { + bHasOwnData = true; + msChartAddress = "all"; + if( !xNewDoc->hasInternalDataProvider() ) + { + xNewDoc->createInternalDataProvider( false /* bCloneExistingData */ ); + SchXMLTableHelper::applyTableToInternalDataProvider( maTable, xNewDoc ); + try + { + lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans ); + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram fallback to internal data failed also" ); + } + } + } + } + } + } + else + { + SAL_WARN("xmloff.chart", "Must not get here" ); + } + + // now all series and data point properties are available and can be set + { + if( bSpecialHandlingForDonutChart ) + { + uno::Reference< chart2::XDiagram > xNewDiagram( xNewDoc->getFirstDiagram() ); + lcl_swapPointAndSeriesStylesForDonutCharts( maSeriesDefaultsAndStyles.maSeriesStyleVector + , SchXMLSeriesHelper::getDataSeriesIndexMapFromDiagram(xNewDiagram) ); + } + + SchXMLSeries2Context::initSeriesPropertySets( maSeriesDefaultsAndStyles, xDoc ); + + //set defaults from diagram to the new series: + //check whether we need to remove lines from symbol only charts + bool bSwitchOffLinesForScatter = false; + { + bool bLinesOn = true; + if( (maSeriesDefaultsAndStyles.maLinesOnProperty >>= bLinesOn) && !bLinesOn ) + { + if( maChartTypeServiceName == "com.sun.star.chart2.ScatterChartType" ) + { + bSwitchOffLinesForScatter = true; + SchXMLSeries2Context::switchSeriesLinesOff( maSeriesDefaultsAndStyles.maSeriesStyleVector ); + } + } + } + SchXMLSeries2Context::setDefaultsToSeries( maSeriesDefaultsAndStyles ); + + // set autostyles for series and data points + const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext(); + const SvXMLStyleContext* pStyle = nullptr; + OUString sCurrStyleName; + + if( pStylesCtxt ) + { + //iterate over data-series first + //don't set series styles for donut charts + if( !bSpecialHandlingForDonutChart ) + { + SchXMLSeries2Context::setStylesToSeries( + maSeriesDefaultsAndStyles, pStylesCtxt, pStyle, + sCurrStyleName, mrImportHelper, GetImport(), + mbIsStockChart, maLSequencesPerIndex ); + // ... then set attributes for statistics (after their existence was set in the series) + SchXMLSeries2Context::setStylesToStatisticsObjects( + maSeriesDefaultsAndStyles, pStylesCtxt, + pStyle, sCurrStyleName ); + + SchXMLSeries2Context::setStylesToRegressionCurves( + maSeriesDefaultsAndStyles, pStylesCtxt, + pStyle, sCurrStyleName ); + } + } + + //#i98319# call switchRangesFromOuterToInternalIfNecessary before the data point styles are applied, otherwise in copy->paste scenario the data point styles do get lost + if( bSwitchRangesFromOuterToInternalIfNecessary ) + { + if( xNewDoc->hasInternalDataProvider() ) + SchXMLTableHelper::switchRangesFromOuterToInternalIfNecessary( maTable, maLSequencesPerIndex, xNewDoc, meDataRowSource ); + } + + if( pStylesCtxt ) + { + // ... then iterate over data-point attributes, so the latter are not overwritten + SchXMLSeries2Context::setStylesToDataPoints( maSeriesDefaultsAndStyles + , pStylesCtxt, pStyle, sCurrStyleName, mrImportHelper, GetImport(), mbIsStockChart, bSpecialHandlingForDonutChart, bSwitchOffLinesForScatter ); + } + } + + if( xProp.is()) + xProp->setPropertyValue("RefreshAddInAllowed", uno::Any( true) ); +} + +void SchXMLChartContext::MergeSeriesForStockChart() +{ + OSL_ASSERT( mbIsStockChart ); + try + { + uno::Reference< chart::XChartDocument > xOldDoc( mrImportHelper.GetChartDocument()); + uno::Reference< chart2::XChartDocument > xDoc( xOldDoc, uno::UNO_QUERY_THROW ); + uno::Reference< chart2::XDiagram > xDiagram( xDoc->getFirstDiagram()); + if( ! xDiagram.is()) + return; + + bool bHasJapaneseCandlestick = true; + uno::Reference< chart2::XDataSeriesContainer > xDSContainer; + uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW ); + const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems()); + for( const auto& rCooSys : aCooSysSeq ) + { + uno::Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY_THROW ); + const uno::Sequence< uno::Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes()); + auto pChartType = std::find_if(aChartTypes.begin(), aChartTypes.end(), + [](const auto& rChartType) { return rChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType"; }); + if (pChartType != aChartTypes.end()) + { + xDSContainer.set( *pChartType, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xCTProp( *pChartType, uno::UNO_QUERY_THROW ); + xCTProp->getPropertyValue("Japanese") >>= bHasJapaneseCandlestick; + } + } + + if( xDSContainer.is()) + { + // with japanese candlesticks: open, low, high, close + // otherwise: low, high, close + uno::Sequence< uno::Reference< chart2::XDataSeries > > aSeriesSeq( xDSContainer->getDataSeries()); + const sal_Int32 nSeriesCount( aSeriesSeq.getLength()); + const sal_Int32 nSeriesPerCandleStick = bHasJapaneseCandlestick ? 4: 3; + sal_Int32 nCandleStickCount = nSeriesCount / nSeriesPerCandleStick; + OSL_ASSERT( nSeriesPerCandleStick * nCandleStickCount == nSeriesCount ); + uno::Sequence< uno::Reference< chart2::XDataSeries > > aNewSeries( nCandleStickCount ); + auto aNewSeriesRange = asNonConstRange(aNewSeries); + for( sal_Int32 i=0; i<nCandleStickCount; ++i ) + { + sal_Int32 nSeriesIndex = i*nSeriesPerCandleStick; + if( bHasJapaneseCandlestick ) + { + // open values + lcl_setRoleAtFirstSequence( aSeriesSeq[ nSeriesIndex ], "values-first"); + aNewSeriesRange[i] = aSeriesSeq[ nSeriesIndex ]; + // low values + lcl_MoveDataToCandleStickSeries( + uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ), + aNewSeries[i], "values-min"); + } + else + { + // low values + lcl_setRoleAtFirstSequence( aSeriesSeq[ nSeriesIndex ], "values-min"); + aNewSeriesRange[i] = aSeriesSeq[ nSeriesIndex ]; + } + // high values + lcl_MoveDataToCandleStickSeries( + uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ), + aNewSeries[i], "values-max"); + // close values + lcl_MoveDataToCandleStickSeries( + uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ), + aNewSeries[i], "values-last"); + } + xDSContainer->setDataSeries( aNewSeries ); + } + } + catch(const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception while merging series for stock chart" ); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLChartContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLImportContext* pContext = nullptr; + uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument(); + uno::Reference< beans::XPropertySet > xProp( xDoc, uno::UNO_QUERY ); + + switch(nElement) + { + case XML_ELEMENT(CHART, XML_PLOT_AREA): + pContext = new SchXMLPlotAreaContext( mrImportHelper, GetImport(), + m_aXLinkHRefAttributeToIndicateDataProvider, + msCategoriesAddress, + msChartAddress, m_bHasRangeAtPlotArea, mbAllRangeAddressesAvailable, + mbColHasLabels, mbRowHasLabels, + meDataRowSource, + maSeriesDefaultsAndStyles, + maChartTypeServiceName, + maLSequencesPerIndex, maChartSize ); + break; + case XML_ELEMENT(CHART, XML_TITLE): + if( xDoc.is()) + { + if( xProp.is()) + { + xProp->setPropertyValue("HasMainTitle", uno::Any(true) ); + } + uno::Reference< drawing::XShape > xTitleShape = xDoc->getTitle(); + pContext = new SchXMLTitleContext( mrImportHelper, GetImport(), + maMainTitle, xTitleShape ); + } + break; + case XML_ELEMENT(CHART, XML_SUBTITLE): + if( xDoc.is()) + { + if( xProp.is()) + { + xProp->setPropertyValue("HasSubTitle", uno::Any(true) ); + } + uno::Reference< drawing::XShape > xTitleShape = xDoc->getSubTitle(); + pContext = new SchXMLTitleContext( mrImportHelper, GetImport(), + maSubTitle, xTitleShape ); + } + break; + case XML_ELEMENT(CHART, XML_LEGEND): + pContext = new SchXMLLegendContext( mrImportHelper, GetImport() ); + break; + case XML_ELEMENT(TABLE, XML_TABLE): + { + SchXMLTableContext * pTableContext = + new SchXMLTableContext( GetImport(), maTable ); + m_bHasTableElement = true; + // #i85913# take into account column- and row- mapping for + // charts with own data only for those which were not copied + // from a place where they got data from the container. Note, + // that this requires the plot-area been read before the table + // (which is required in the ODF spec) + // Note: For stock charts and donut charts with special handling + // the mapping must not be applied! + if( msChartAddress.isEmpty() && !mbIsStockChart && + !lcl_SpecialHandlingForDonutChartNeeded( + maChartTypeServiceName, GetImport())) + { + if( !msColTrans.isEmpty() ) + { + OSL_ASSERT( msRowTrans.isEmpty() ); + pTableContext->setColumnPermutation( lcl_getNumberSequenceFromString( msColTrans, true )); + msColTrans.clear(); + } + else if( !msRowTrans.isEmpty() ) + { + pTableContext->setRowPermutation( lcl_getNumberSequenceFromString( msRowTrans, true )); + msRowTrans.clear(); + } + } + pContext = pTableContext; + } + break; + + default: + // try importing as an additional shape + if( ! mxDrawPage.is()) + { + uno::Reference< drawing::XDrawPageSupplier > xSupp( xDoc, uno::UNO_QUERY ); + if( xSupp.is()) + mxDrawPage = xSupp->getDrawPage(); + + SAL_WARN_IF( !mxDrawPage.is(), "xmloff.chart", "Invalid Chart Page" ); + } + if( mxDrawPage.is()) + pContext = XMLShapeImportHelper::CreateGroupChildContext( + GetImport(), nElement, xAttrList, mxDrawPage ); + break; + } + + return pContext; +} + +/* + With a locked controller the following is done here: + 1. Hide title, subtitle, and legend. + 2. Set the size of the draw page. + 3. Set a (logically) empty data set. + 4. Set the chart type. +*/ +void SchXMLChartContext::InitChart( + const OUString & rChartTypeServiceName // currently the old service name + ) +{ + uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument(); + SAL_WARN_IF( !xDoc.is(), "xmloff.chart", "No valid document!" ); + + // Remove Title and Diagram ("De-InitNew") + uno::Reference< chart2::XChartDocument > xNewDoc( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ); + if( xNewDoc.is()) + { + xNewDoc->setFirstDiagram( nullptr ); + uno::Reference< chart2::XTitled > xTitled( xNewDoc, uno::UNO_QUERY ); + if( xTitled.is()) + xTitled->setTitleObject( nullptr ); + } + + // Set the chart type via setting the diagram. + if( !rChartTypeServiceName.isEmpty() && xDoc.is()) + { + uno::Reference< lang::XMultiServiceFactory > xFact( xDoc, uno::UNO_QUERY ); + if( xFact.is()) + { + uno::Reference< chart::XDiagram > xDia( xFact->createInstance( rChartTypeServiceName ), uno::UNO_QUERY ); + if( xDia.is()) + xDoc->setDiagram( xDia ); + } + } +} + +SchXMLTitleContext::SchXMLTitleContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, + OUString& rTitle, + uno::Reference< drawing::XShape > const & xTitleShape ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mrTitle( rTitle ), + mxTitleShape( xTitleShape ) +{ +} + +SchXMLTitleContext::~SchXMLTitleContext() +{} + +void SchXMLTitleContext::startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + css::awt::Point aPosition; + bool bHasXPosition=false; + bool bHasYPosition=false; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(SVG, XML_X): + case XML_ELEMENT(SVG_COMPAT, XML_X): + { + GetImport().GetMM100UnitConverter().convertMeasureToCore( + aPosition.X, aIter.toView() ); + bHasXPosition = true; + break; + } + case XML_ELEMENT(SVG, XML_Y): + case XML_ELEMENT(SVG_COMPAT, XML_Y): + { + GetImport().GetMM100UnitConverter().convertMeasureToCore( + aPosition.Y, aIter.toView() ); + bHasYPosition = true; + break; + } + case XML_ELEMENT(CHART, XML_STYLE_NAME): + msAutoStyleName = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + if( mxTitleShape.is()) + { + if( bHasXPosition && bHasYPosition ) + mxTitleShape->setPosition( aPosition ); + + uno::Reference<beans::XPropertySet> xProp(mxTitleShape, uno::UNO_QUERY); + mrImportHelper.FillAutoStyle(msAutoStyleName, xProp); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTitleContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + SvXMLImportContext* pContext = nullptr; + + if( nElement == XML_ELEMENT(TEXT, XML_P) || + nElement == XML_ELEMENT(LO_EXT, XML_P) ) + { + pContext = new SchXMLParagraphContext( GetImport(), mrTitle ); + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + + return pContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLChartContext.hxx b/xmloff/source/chart/SchXMLChartContext.hxx new file mode 100644 index 000000000..53402168b --- /dev/null +++ b/xmloff/source/chart/SchXMLChartContext.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlictxt.hxx> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/awt/Size.hpp> + +#include "transporttypes.hxx" + +#include <vector> + +class SchXMLImportHelper; + +namespace com::sun::star { + namespace chart { + class XChartDocument; + struct ChartSeriesAddress; + } + namespace xml::sax { + class XAttributeList; + } + namespace drawing { + class XShapes; + } +} + +struct SeriesDefaultsAndStyles +{ + //default values for series: + css::uno::Any maSymbolTypeDefault; + css::uno::Any maDataCaptionDefault; + + css::uno::Any maErrorIndicatorDefault; + css::uno::Any maErrorCategoryDefault; + css::uno::Any maConstantErrorLowDefault; + css::uno::Any maConstantErrorHighDefault; + css::uno::Any maPercentageErrorDefault; + css::uno::Any maErrorMarginDefault; + + css::uno::Any maMeanValueDefault; + css::uno::Any maRegressionCurvesDefault; + + css::uno::Any maStackedDefault; + css::uno::Any maPercentDefault; + css::uno::Any maDeepDefault; + css::uno::Any maStackedBarsConnectedDefault; + + //additional information + css::uno::Any maLinesOnProperty; + + //styles for series and datapoints + ::std::vector< DataRowPointStyle > maSeriesStyleVector; + ::std::vector< RegressionStyle > maRegressionStyleVector; +}; + +class SchXMLChartContext : public SvXMLImportContext +{ +public: + SchXMLChartContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport ); + virtual ~SchXMLChartContext() override; + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + +private: + SchXMLTable maTable; + SchXMLImportHelper& mrImportHelper; + + OUString maMainTitle, maSubTitle; + OUString m_aXLinkHRefAttributeToIndicateDataProvider; + bool m_bHasRangeAtPlotArea; + bool m_bHasTableElement; + bool mbAllRangeAddressesAvailable; + bool mbColHasLabels; + bool mbRowHasLabels; + css::chart::ChartDataRowSource meDataRowSource; + bool mbIsStockChart; + + OUString msCategoriesAddress; + OUString msChartAddress; + + OUString msDataPilotSource; + + SeriesDefaultsAndStyles maSeriesDefaultsAndStyles; + tSchXMLLSequencesPerIndex maLSequencesPerIndex; + + css::uno::Reference< css::drawing::XShapes > mxDrawPage; + OUString msColTrans; + OUString msRowTrans; + OUString maChartTypeServiceName; + + css::awt::Size maChartSize; + + /** @descr This method bundles some settings to the chart model and executes them with + a locked controller. This includes setting the chart type. + @param aServiceName The name of the service the diagram is initialized with. + */ + void InitChart (const OUString & rChartTypeServiceName); + + void MergeSeriesForStockChart(); +}; + +class SchXMLTitleContext : public SvXMLImportContext +{ +private: + SchXMLImportHelper& mrImportHelper; + OUString& mrTitle; + css::uno::Reference< css::drawing::XShape > mxTitleShape; + OUString msAutoStyleName; + +public: + SchXMLTitleContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + OUString& rTitle, + css::uno::Reference< css::drawing::XShape > const & xTitleShape ); + virtual ~SchXMLTitleContext() override; + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLEnumConverter.cxx b/xmloff/source/chart/SchXMLEnumConverter.cxx new file mode 100644 index 000000000..7bb888ee0 --- /dev/null +++ b/xmloff/source/chart/SchXMLEnumConverter.cxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/chart/ChartLegendPosition.hpp> +#include <com/sun/star/chart/ChartLegendExpansion.hpp> +#include "SchXMLEnumConverter.hxx" +#include <xmloff/xmlement.hxx> + +using namespace ::xmloff::token; +using namespace ::com::sun::star; + +namespace +{ + +const SvXMLEnumMapEntry<chart::ChartLegendPosition> aXMLLegendPositionEnumMap[] = +{ + { XML_START, chart::ChartLegendPosition_LEFT }, + { XML_TOP, chart::ChartLegendPosition_TOP }, + { XML_END, chart::ChartLegendPosition_RIGHT }, + { XML_BOTTOM, chart::ChartLegendPosition_BOTTOM }, + { XML_TOKEN_INVALID, chart::ChartLegendPosition(0) } +}; + +class XMLLegendPositionPropertyHdl : public XMLEnumPropertyHdl +{ +public: + XMLLegendPositionPropertyHdl() + : XMLEnumPropertyHdl( aXMLLegendPositionEnumMap) {} +}; + +const SvXMLEnumMapEntry<chart::ChartLegendExpansion> aXMLLegendExpansionEnumMap[] = +{ + { XML_WIDE, chart::ChartLegendExpansion_WIDE }, + { XML_HIGH, chart::ChartLegendExpansion_HIGH }, + { XML_BALANCED, chart::ChartLegendExpansion_BALANCED }, + { XML_CUSTOM, chart::ChartLegendExpansion_CUSTOM }, + { XML_TOKEN_INVALID, chart::ChartLegendExpansion(0) } +}; + +class XMLLegendExpansionPropertyHdl : public XMLEnumPropertyHdl +{ +public: + XMLLegendExpansionPropertyHdl() + : XMLEnumPropertyHdl( aXMLLegendExpansionEnumMap) {} +}; + +}//end anonymous namespace + +XMLEnumPropertyHdl& SchXMLEnumConverter::getLegendPositionConverter() +{ + static XMLLegendPositionPropertyHdl SINGLETON; + return SINGLETON; +} +XMLEnumPropertyHdl& SchXMLEnumConverter::getLegendExpansionConverter() +{ + static XMLLegendExpansionPropertyHdl SINGLETON; + return SINGLETON; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLEnumConverter.hxx b/xmloff/source/chart/SchXMLEnumConverter.hxx new file mode 100644 index 000000000..a61a611b0 --- /dev/null +++ b/xmloff/source/chart/SchXMLEnumConverter.hxx @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/EnumPropertyHdl.hxx> + +class SchXMLEnumConverter +{ +public: + static XMLEnumPropertyHdl& getLegendPositionConverter(); //returns a singleton + static XMLEnumPropertyHdl& getLegendExpansionConverter(); //returns a singleton +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLExport.cxx b/xmloff/source/chart/SchXMLExport.cxx new file mode 100644 index 000000000..fc0a24a8e --- /dev/null +++ b/xmloff/source/chart/SchXMLExport.cxx @@ -0,0 +1,4067 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <sal/config.h> +#include <sal/log.hxx> + +#include <sax/tools/converter.hxx> + +#include <xmloff/xmlprmap.hxx> + +#include <SchXMLExport.hxx> +#include <XMLChartPropertySetMapper.hxx> +#include "ColorPropertySet.hxx" +#include "SchXMLTools.hxx" +#include "SchXMLEnumConverter.hxx" + +#include <comphelper/processfactory.hxx> +#include <tools/globname.hxx> +#include <comphelper/classids.hxx> +#include <comphelper/sequence.hxx> + +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/families.hxx> +#include <xmloff/xmlaustp.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/SchXMLSeriesHelper.hxx> +#include <rtl/math.hxx> +#include <o3tl/sorted_vector.hxx> +#include <o3tl/string_view.hxx> + +#include <limits> +#include <vector> +#include <algorithm> +#include <queue> +#include <iterator> +#include <numeric> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XServiceName.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/XRefreshable.hpp> + +#include <com/sun/star/chart/XAxis.hpp> +#include <com/sun/star/chart/XAxisSupplier.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart/ChartLegendExpansion.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart/ChartAxisAssign.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart/TimeIncrement.hpp> +#include <com/sun/star/chart/TimeInterval.hpp> +#include <com/sun/star/chart/TimeUnit.hpp> +#include <com/sun/star/chart/X3DDisplay.hpp> +#include <com/sun/star/chart/XStatisticDisplay.hpp> +#include <com/sun/star/chart/XDiagramPositioning.hpp> + +#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp> +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XDiagram.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp> +#include <com/sun/star/chart2/data/XDataSource.hpp> +#include <com/sun/star/chart2/data/XDataSink.hpp> +#include <com/sun/star/chart2/data/XDataProvider.hpp> +#include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp> +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> +#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp> +#include <com/sun/star/chart2/data/XTextualDataSequence.hpp> +#include <com/sun/star/chart2/data/XNumericalDataSequence.hpp> + +#include <com/sun/star/util/MeasureUnit.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XVisualObject.hpp> +#include <com/sun/star/container/XChild.hpp> + +#include <tools/diagnose_ex.h> +#include "MultiPropertySetHandler.hxx" +#include "PropertyMap.hxx" + +using namespace com::sun::star; +using namespace ::xmloff::token; + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Any; +using ::std::vector; + + +namespace +{ + /** + * Used to store a data point custom-label's fields and also the helper members that + * indicates whether this label's contents are sourced from a cell[range] and + * the address of the cell[range] with guid of the CELLRANGE field. + */ + struct CustomLabelData + { + CustomLabelData(): + mbDataLabelsRange( false ) + { + } + + /// Label fields. + Sequence<Reference<chart2::XDataPointCustomLabelField>> maFields; + /// Are label's contents sourced from a cell[range] ? + bool mbDataLabelsRange; + /// cell[range] from which label's contents are sourced. + OUString maRange; + /// GUID of the CELLRANGE field. + OUString maGuid; + }; + + struct SchXMLDataPointStruct + { + OUString maStyleName; + sal_Int32 mnRepeat; + chart2::RelativePosition mCustomLabelPos; // loext:custom-label-pos-x and -y + + // There is no internal equivalent for <chart:data-label>. It will be generated on the fly + // on export. All about data label is hold in the data point. + CustomLabelData mCustomLabel; // <text:p> child element in <chart:data-label> + OUString msDataLabelStyleName; // chart:style-name attribute in <chart:data-label> + + SchXMLDataPointStruct() : mnRepeat( 1 ) {} + }; +} + + +class SchXMLExportHelper_Impl +{ +public: + // first: data sequence for label, second: data sequence for values. + typedef ::std::pair< css::uno::Reference< css::chart2::data::XDataSequence >, + css::uno::Reference< css::chart2::data::XDataSequence > > tLabelValuesDataPair; + typedef ::std::vector< tLabelValuesDataPair > tDataSequenceCont; + +public: + SchXMLExportHelper_Impl( SvXMLExport& rExport, + SvXMLAutoStylePoolP& rASPool ); + + SchXMLExportHelper_Impl(const SchXMLExportHelper_Impl&) = delete; + SchXMLExportHelper_Impl& operator=(const SchXMLExportHelper_Impl&) = delete; + + // auto-styles + /// parse chart and collect all auto-styles used in current pool + void collectAutoStyles( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc ); + + /// write the styles collected into the current pool as <style:style> elements + void exportAutoStyles(); + + /** export the <chart:chart> element corresponding to rChartDoc + if bIncludeTable is true, the chart data is exported as <table:table> + element (inside the chart element). + + Otherwise the external references stored in the chart document are used + for writing the corresponding attributes at series + + All attributes contained in xAttrList are written at the chart element, + which is the outer element of a chart. So these attributes can easily + be parsed again by the container + */ + void exportChart( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc, + bool bIncludeTable ); + + const rtl::Reference<XMLPropertySetMapper>& GetPropertySetMapper() const; + + void SetChartRangeAddress( const OUString& rAddress ) + { msChartAddress = rAddress; } + + void InitRangeSegmentationProperties( + const css::uno::Reference< css::chart2::XChartDocument > & xChartDoc ); + + static css::awt::Size getPageSize( + const css::uno::Reference< css::chart2::XChartDocument > & xChartDoc ); + + /** first parseDocument: collect autostyles and store names in this queue + second parseDocument: export content and use names from this queue + */ + ::std::queue< OUString > maAutoStyleNameQueue; + void CollectAutoStyle( + std::vector< XMLPropertyState >&& aStates ); + void AddAutoStyleAttribute( + const std::vector< XMLPropertyState >& aStates ); + + /// if bExportContent is false the auto-styles are collected + void parseDocument( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc, + bool bExportContent, + bool bIncludeTable = false ); + void exportTable(); + void exportPlotArea( + const css::uno::Reference< css::chart::XDiagram >& xDiagram, + const css::uno::Reference< css::chart2::XDiagram >& xNewDiagram, + const css::awt::Size & rPageSize, + bool bExportContent, + bool bIncludeTable ); + void exportCoordinateRegion( const css::uno::Reference< css::chart::XDiagram >& xDiagram ); + void exportAxes( const css::uno::Reference< css::chart::XDiagram > & xDiagram, + const css::uno::Reference< css::chart2::XDiagram > & xNewDiagram, + bool bExportContent ); + void exportAxis( enum XMLTokenEnum eDimension, enum XMLTokenEnum eAxisName, + const Reference< beans::XPropertySet >& rAxisProps, const Reference< chart2::XAxis >& rChart2Axis, + const OUString& rCategoriesRanges, + bool bHasTitle, bool bHasMajorGrid, bool bHasMinorGrid, bool bExportContent, std::u16string_view sChartType ); + void exportGrid( const Reference< beans::XPropertySet >& rGridProperties, bool bMajor, bool bExportContent ); + void exportDateScale( const Reference< beans::XPropertySet >& rAxisProps ); + void exportAxisTitle( const Reference< beans::XPropertySet >& rTitleProps, bool bExportContent ); + + void exportSeries( + const css::uno::Reference< css::chart2::XDiagram > & xNewDiagram, + const css::awt::Size & rPageSize, + bool bExportContent, + bool bHasTwoYAxes ); + + void exportPropertyMapping( + const css::uno::Reference< css::chart2::data::XDataSource > & xSource, + const Sequence< OUString >& rSupportedMappings ); + + void exportCandleStickSeries( + const css::uno::Sequence< + css::uno::Reference< css::chart2::XDataSeries > > & aSeriesSeq, + const css::uno::Reference< css::chart2::XDiagram > & xDiagram, + bool bJapaneseCandleSticks, + bool bExportContent ); + void exportDataPoints( + const css::uno::Reference< css::beans::XPropertySet > & xSeriesProperties, + sal_Int32 nSeriesLength, + const css::uno::Reference< css::chart2::XDiagram > & xDiagram, + bool bExportContent ); + + void exportCustomLabel(const SchXMLDataPointStruct& rPoint); + void exportCustomLabelPosition(const chart2::RelativePosition& xCustomLabelPosition); + + void exportRegressionCurve( + const css::uno::Reference<css::chart2::XDataSeries>& xSeries, + const css::awt::Size& rPageSize, + bool bExportContent ); + + void exportErrorBar ( + const css::uno::Reference<beans::XPropertySet> &xSeriesProp, bool bYError, + bool bExportContent ); + + /// add svg position as attribute for current element + void addPosition( const css::awt::Point & rPosition ); + void addPosition( const css::uno::Reference< css::drawing::XShape >& xShape ); + /// add svg size as attribute for current element + void addSize( const css::awt::Size & rSize, bool bIsOOoNamespace = false ); + void addSize( const css::uno::Reference< css::drawing::XShape >& xShape ); + /// exports a string as a paragraph element + void exportText( const OUString& rText ); + +public: + SvXMLExport& mrExport; + SvXMLAutoStylePoolP& mrAutoStylePool; + rtl::Reference< XMLPropertySetMapper > mxPropertySetMapper; + rtl::Reference< XMLChartExportPropertyMapper > mxExpPropMapper; + + static constexpr OUStringLiteral gsTableName = u"local-table"; + OUStringBuffer msStringBuffer; + OUString msString; + + // members filled by InitRangeSegmentationProperties (retrieved from DataProvider) + bool mbHasCategoryLabels; //if the categories are only automatically generated this will be false + bool mbRowSourceColumns; + OUString msChartAddress; + css::uno::Sequence< sal_Int32 > maSequenceMapping; + + OUString msCLSID; + + OUString maSrcShellID; + OUString maDestShellID; + + css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes; + + tDataSequenceCont m_aDataSequencesToExport; + OUString maCategoriesRange; +}; + +namespace +{ +CustomLabelData lcl_getCustomLabelField(SvXMLExport const& rExport, + sal_Int32 nDataPointIndex, + const uno::Reference< chart2::XDataSeries >& rSeries) +{ + if (!rSeries.is()) + return CustomLabelData(); + + // Custom data label text will be written to the <text:p> child element of a + // <chart:data-label> element. That exists only since ODF 1.2. + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + rExport.getSaneDefaultVersion()); + if (nCurrentODFVersion < SvtSaveOptions::ODFSVER_012) + return CustomLabelData(); + + if(Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is()) + { + if(Any aAny = xLabels->getPropertyValue("CustomLabelFields"); aAny.hasValue()) + { + CustomLabelData aData; + Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aCustomLabels; + aAny >>= aCustomLabels; + for (const auto& rField: std::as_const(aCustomLabels)) + { + if (rField->getFieldType() == chart2::DataPointCustomLabelFieldType_CELLRANGE) + { + if (rField->getDataLabelsRange()) + aData.mbDataLabelsRange = true; + aData.maRange = rField->getCellRange(); + aData.maGuid = rField->getGuid(); + } + } + + aData.maFields = aCustomLabels; + return aData; + } + } + return CustomLabelData(); +} + +css::chart2::RelativePosition lcl_getCustomLabelPosition( + SvXMLExport const& rExport, + sal_Int32 const nDataPointIndex, + const uno::Reference< chart2::XDataSeries >& rSeries) +{ + if (!rSeries.is()) + return chart2::RelativePosition(); + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + rExport.getSaneDefaultVersion()); + + if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0) // do not export to ODF 1.3 or older + return chart2::RelativePosition(); + + if (Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is()) + { + if (Any aAny = xLabels->getPropertyValue("CustomLabelPosition"); aAny.hasValue()) + { + chart2::RelativePosition aCustomLabelPos; + aAny >>= aCustomLabelPos; + return aCustomLabelPos; + } + } + return chart2::RelativePosition(); +} + +class lcl_MatchesRole +{ +public: + explicit lcl_MatchesRole( const OUString & aRole ) : + m_aRole( aRole ) + {} + + bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const + { + if( !xSeq.is() ) + return false; + Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY ); + OUString aRole; + + return ( xProp.is() && + (xProp->getPropertyValue( "Role" ) >>= aRole ) && + m_aRole == aRole ); + } + +private: + OUString m_aRole; +}; + +Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( const Reference< chart2::XDiagram > & xDiagram ) +{ + Reference< chart2::data::XLabeledDataSequence > xResult; + try + { + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( + xDiagram, uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( + xCooSysCnt->getCoordinateSystems()); + for( const auto& rCooSys : aCooSysSeq ) + { + Reference< chart2::XCoordinateSystem > xCooSys( rCooSys ); + SAL_WARN_IF( !xCooSys.is(), "xmloff.chart", "xCooSys is NULL" ); + for( sal_Int32 nN = xCooSys->getDimension(); nN--; ) + { + const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN); + for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI) + { + Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI ); + SAL_WARN_IF( !xAxis.is(), "xmloff.chart", "xAxis is NULL"); + if( xAxis.is()) + { + chart2::ScaleData aScaleData = xAxis->getScaleData(); + if( aScaleData.Categories.is()) + { + xResult.set( aScaleData.Categories ); + break; + } + } + } + } + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + + return xResult; +} + +Reference< chart2::data::XDataSource > lcl_createDataSource( + const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aData ) +{ + Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< chart2::data::XDataSink > xSink( + xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.chart2.data.DataSource", xContext ), + uno::UNO_QUERY_THROW ); + xSink->setData( aData ); + + return Reference< chart2::data::XDataSource >( xSink, uno::UNO_QUERY ); +} + +Sequence< Reference< chart2::data::XLabeledDataSequence > > lcl_getAllSeriesSequences( const Reference< chart2::XChartDocument >& xChartDoc ) +{ + ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aContainer; + if( xChartDoc.is() ) + { + Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram()); + ::std::vector< Reference< chart2::XDataSeries > > aSeriesVector( SchXMLSeriesHelper::getDataSeriesFromDiagram( xDiagram )); + for( const auto& rSeries : aSeriesVector ) + { + Reference< chart2::data::XDataSource > xDataSource( rSeries, uno::UNO_QUERY ); + if( !xDataSource.is() ) + continue; + const uno::Sequence< Reference< chart2::data::XLabeledDataSequence > > aDataSequences( xDataSource->getDataSequences() ); + aContainer.insert( aContainer.end(), aDataSequences.begin(), aDataSequences.end() ); + } + } + + return comphelper::containerToSequence( aContainer ); +} + +Reference< chart2::data::XLabeledDataSequence > + lcl_getDataSequenceByRole( + const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq, + const OUString & rRole ) +{ + Reference< chart2::data::XLabeledDataSequence > aNoResult; + + const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray(); + const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength(); + const Reference< chart2::data::XLabeledDataSequence > * pMatch = + ::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole )); + + if( pMatch != pEnd ) + return *pMatch; + + return aNoResult; +} + +Reference< chart2::data::XDataSource > lcl_pressUsedDataIntoRectangularFormat( const Reference< chart2::XChartDocument >& xChartDoc, bool& rOutSourceHasCategoryLabels ) +{ + ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aLabeledSeqVector; + + //categories are always the first sequence + Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram()); + Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram ) ); + if( xCategories.is() ) + aLabeledSeqVector.push_back( xCategories ); + rOutSourceHasCategoryLabels = xCategories.is(); + + const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeriesSeqVector( + lcl_getAllSeriesSequences( xChartDoc ) ); + + //the first x-values is always the next sequence //todo ... other x-values get lost for old format + Reference< chart2::data::XLabeledDataSequence > xXValues( + lcl_getDataSequenceByRole( aSeriesSeqVector, "values-x" ) ); + if( xXValues.is() ) + aLabeledSeqVector.push_back( xXValues ); + + //add all other sequences now without x-values + lcl_MatchesRole aHasXValues( "values-x" ); + std::copy_if(aSeriesSeqVector.begin(), aSeriesSeqVector.end(), std::back_inserter(aLabeledSeqVector), + [&aHasXValues](const auto& rSeriesSeq) { return !aHasXValues( rSeriesSeq ); }); + + Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( comphelper::containerToSequence(aLabeledSeqVector) ); + + return lcl_createDataSource( aSeq ); +} + +bool lcl_isSeriesAttachedToFirstAxis( + const Reference< chart2::XDataSeries > & xDataSeries ) +{ + bool bResult=true; + + try + { + sal_Int32 nAxisIndex = 0; + Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW ); + xProp->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex; + bResult = (0==nAxisIndex); + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + + return bResult; +} + +OUString lcl_ConvertRange( const OUString & rRange, const Reference< chart2::XChartDocument > & xDoc ) +{ + OUString aResult = rRange; + if( !xDoc.is() ) + return aResult; + Reference< chart2::data::XRangeXMLConversion > xConversion( + xDoc->getDataProvider(), uno::UNO_QUERY ); + if( xConversion.is()) + aResult = xConversion->convertRangeToXML( rRange ); + return aResult; +} + +typedef ::std::pair< OUString, OUString > tLabelAndValueRange; + +tLabelAndValueRange lcl_getLabelAndValueRangeByRole( + const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aSeqCnt, + const OUString & rRole, + const Reference< chart2::XChartDocument > & xDoc, + SchXMLExportHelper_Impl::tDataSequenceCont & rOutSequencesToExport ) +{ + tLabelAndValueRange aResult; + + Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( + lcl_getDataSequenceByRole( aSeqCnt, rRole )); + if( xLabeledSeq.is()) + { + Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel()); + if( xLabelSeq.is()) + aResult.first = lcl_ConvertRange( xLabelSeq->getSourceRangeRepresentation(), xDoc ); + + Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues()); + if( xValueSeq.is()) + aResult.second = lcl_ConvertRange( xValueSeq->getSourceRangeRepresentation(), xDoc ); + + if( xLabelSeq.is() || xValueSeq.is()) + rOutSequencesToExport.emplace_back( xLabelSeq, xValueSeq ); + } + + return aResult; +} + +sal_Int32 lcl_getSequenceLengthByRole( + const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aSeqCnt, + const OUString & rRole ) +{ + Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( + lcl_getDataSequenceByRole( aSeqCnt, rRole )); + if( xLabeledSeq.is()) + { + Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getValues()); + return xSeq->getData().getLength(); + } + return 0; +} + +OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence ) +{ + OUStringBuffer aResult; + bool bPrecedeWithSpace = false; + for( const auto& rString : rSequence ) + { + if( !rString.isEmpty()) + { + if( bPrecedeWithSpace ) + aResult.append( ' ' ); + aResult.append( rString ); + bPrecedeWithSpace = true; + } + } + return aResult.makeStringAndClear(); +} + +void lcl_getLabelStringSequence( Sequence< OUString >& rOutLabels, const Reference< chart2::data::XDataSequence > & xLabelSeq ) +{ + uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY ); + if( xTextualDataSequence.is()) + { + rOutLabels = xTextualDataSequence->getTextualData(); + } + else if( xLabelSeq.is()) + { + Sequence< uno::Any > aAnies( xLabelSeq->getData()); + rOutLabels.realloc( aAnies.getLength()); + auto pOutLabels = rOutLabels.getArray(); + for( sal_Int32 i=0; i<aAnies.getLength(); ++i ) + aAnies[i] >>= pOutLabels[i]; + } +} + +sal_Int32 lcl_getMaxSequenceLength( + const SchXMLExportHelper_Impl::tDataSequenceCont & rContainer ) +{ + sal_Int32 nResult = 0; + for( const auto& rDataSequence : rContainer ) + { + if( rDataSequence.second.is()) + { + sal_Int32 nSeqLength = rDataSequence.second->getData().getLength(); + if( nSeqLength > nResult ) + nResult = nSeqLength; + } + } + return nResult; +} + +uno::Sequence< OUString > lcl_DataSequenceToStringSequence( + const uno::Reference< chart2::data::XDataSequence >& xDataSequence ) +{ + uno::Sequence< OUString > aResult; + if(!xDataSequence.is()) + return aResult; + + uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY ); + if( xTextualDataSequence.is() ) + { + aResult = xTextualDataSequence->getTextualData(); + } + else + { + uno::Sequence< uno::Any > aValues = xDataSequence->getData(); + aResult.realloc(aValues.getLength()); + auto pResult = aResult.getArray(); + + for(sal_Int32 nN=aValues.getLength();nN--;) + aValues[nN] >>= pResult[nN]; + } + + return aResult; +} +::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq ) +{ + ::std::vector< double > aResult; + if(!xSeq.is()) + return aResult; + + uno::Sequence< double > aValuesSequence; + Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY ); + if( xNumSeq.is() ) + { + aValuesSequence = xNumSeq->getNumericalData(); + } + else + { + Sequence< uno::Any > aAnies( xSeq->getData() ); + aValuesSequence.realloc( aAnies.getLength() ); + auto pValuesSequence = aValuesSequence.getArray(); + for( sal_Int32 i=0; i<aAnies.getLength(); ++i ) + aAnies[i] >>= pValuesSequence[i]; + } + + //special handling for x-values (if x-values do point to categories, indices are used instead ) + Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY ); + if( xProp.is() ) + { + OUString aRole; + xProp->getPropertyValue("Role") >>= aRole; + if( aRole.match("values-x") ) + { + //lcl_clearIfNoValuesButTextIsContained - replace by indices if the values are not appropriate + bool bHasValue = std::any_of(std::cbegin(aValuesSequence), std::cend(aValuesSequence), + [](double fValue) { return !std::isnan( fValue ); }); + if(!bHasValue) + { + //no double value is contained + //is there any text? + const uno::Sequence< OUString > aStrings( lcl_DataSequenceToStringSequence( xSeq ) ); + bool bHasText = std::any_of(aStrings.begin(), aStrings.end(), + [](const OUString& rString) { return !rString.isEmpty(); }); + if( bHasText ) + { + auto [begin, end] = asNonConstRange(aValuesSequence); + std::iota(begin, end, 1); + } + } + } + } + + aResult.insert( aResult.end(), std::cbegin(aValuesSequence), std::cend(aValuesSequence) ); + return aResult; +} + +bool lcl_SequenceHasUnhiddenData( const uno::Reference< chart2::data::XDataSequence >& xDataSequence ) +{ + if( !xDataSequence.is() ) + return false; + uno::Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY ); + if( xProp.is() ) + { + uno::Sequence< sal_Int32 > aHiddenValues; + try + { + xProp->getPropertyValue("HiddenValues") >>= aHiddenValues; + if( !aHiddenValues.hasElements() ) + return true; + } + catch( const uno::Exception& ) + { + return true; + } + } + return xDataSequence->getData().hasElements(); +} + +typedef vector< OUString > tStringVector; +typedef vector< vector< double > > t2DNumberContainer; + +struct lcl_TableData +{ + t2DNumberContainer aDataInRows; + tStringVector aDataRangeRepresentations; + + tStringVector aColumnDescriptions; + tStringVector aColumnDescriptions_Ranges; + + tStringVector aRowDescriptions; + tStringVector aRowDescriptions_Ranges; + + Sequence< Sequence< uno::Any > > aComplexColumnDescriptions;//outer index is columns - inner index is level + Sequence< Sequence< uno::Any > > aComplexRowDescriptions;//outer index is rows - inner index is level + + ::std::vector< sal_Int32 > aHiddenColumns; +}; + +typedef ::std::map< sal_Int32, SchXMLExportHelper_Impl::tLabelValuesDataPair > + lcl_DataSequenceMap; + +struct lcl_SequenceToMapElement +{ + std::pair<const sal_Int32, SchXMLExportHelper_Impl::tLabelValuesDataPair> + operator() (const SchXMLExportHelper_Impl::tLabelValuesDataPair& rContent) + { + sal_Int32 nIndex = -1; + if( rContent.second.is()) //has values + { + OUString aRangeRep( rContent.second->getSourceRangeRepresentation()); + nIndex = aRangeRep.toInt32(); + } + else if( rContent.first.is()) //has labels + nIndex = o3tl::toInt32(rContent.first->getSourceRangeRepresentation().subView( sizeof("label "))); + return std::make_pair(nIndex, rContent); + } +}; + +void lcl_ReorderInternalSequencesAccordingToTheirRangeName( + SchXMLExportHelper_Impl::tDataSequenceCont & rInOutSequences ) +{ + lcl_DataSequenceMap aIndexSequenceMap; + ::std::transform( rInOutSequences.begin(), rInOutSequences.end(), + ::std::inserter( aIndexSequenceMap, aIndexSequenceMap.begin()), + lcl_SequenceToMapElement()); + + rInOutSequences.clear(); + sal_Int32 nIndex = 0; + for( const auto& rEntry : aIndexSequenceMap ) + { + if( rEntry.first >= 0 ) + { + // fill empty columns + rInOutSequences.insert( + rInOutSequences.end(), + rEntry.first - nIndex, + SchXMLExportHelper_Impl::tDataSequenceCont::value_type( + uno::Reference< chart2::data::XDataSequence >(), + uno::Reference< chart2::data::XDataSequence >() )); + nIndex = rEntry.first; + rInOutSequences.push_back( rEntry.second ); + } + + ++nIndex; + } +} + +lcl_TableData lcl_getDataForLocalTable( + const SchXMLExportHelper_Impl::tDataSequenceCont & aSequencesToExport, + const Reference< chart2::XAnyDescriptionAccess >& xAnyDescriptionAccess, + const OUString& rCategoriesRange, + bool bSeriesFromColumns, + const Reference< chart2::data::XRangeXMLConversion > & xRangeConversion ) +{ + lcl_TableData aResult; + + try + { + Sequence< OUString > aSimpleCategories; + if( xAnyDescriptionAccess.is() ) + { + //categories + if( bSeriesFromColumns ) + { + aSimpleCategories = xAnyDescriptionAccess->getRowDescriptions(); + aResult.aComplexRowDescriptions = xAnyDescriptionAccess->getAnyRowDescriptions(); + } + else + { + aSimpleCategories = xAnyDescriptionAccess->getColumnDescriptions(); + aResult.aComplexColumnDescriptions = xAnyDescriptionAccess->getAnyColumnDescriptions(); + } + } + + //series values and series labels + SchXMLExportHelper_Impl::tDataSequenceCont::size_type nNumSequences = aSequencesToExport.size(); + + auto nMaxSequenceLength( lcl_getMaxSequenceLength( aSequencesToExport )); + if( aSimpleCategories.getLength() > nMaxSequenceLength ) + { + aSimpleCategories.realloc(nMaxSequenceLength);//#i110617# + } + size_t nNumColumns( bSeriesFromColumns ? nNumSequences : nMaxSequenceLength ); + size_t nNumRows( bSeriesFromColumns ? nMaxSequenceLength : nNumSequences ); + + // resize data + aResult.aDataInRows.resize( nNumRows ); + + for (auto& aData: aResult.aDataInRows) + aData.resize(nNumColumns, std::numeric_limits<double>::quiet_NaN()); + aResult.aColumnDescriptions.resize( nNumColumns ); + aResult.aComplexColumnDescriptions.realloc( nNumColumns ); + aResult.aRowDescriptions.resize( nNumRows ); + aResult.aComplexRowDescriptions.realloc( nNumRows ); + + tStringVector& rCategories = bSeriesFromColumns ? aResult.aRowDescriptions : aResult.aColumnDescriptions; + tStringVector& rLabels = bSeriesFromColumns ? aResult.aColumnDescriptions : aResult.aRowDescriptions; + + //categories + rCategories.clear(); + rCategories.insert( rCategories.begin(), std::cbegin(aSimpleCategories), std::cend(aSimpleCategories) ); + if( !rCategoriesRange.isEmpty() ) + { + OUString aRange(rCategoriesRange); + if( xRangeConversion.is()) + aRange = xRangeConversion->convertRangeToXML( aRange ); + if( bSeriesFromColumns ) + aResult.aRowDescriptions_Ranges.push_back( aRange ); + else + aResult.aColumnDescriptions_Ranges.push_back( aRange ); + } + + // iterate over all sequences + size_t nSeqIdx = 0; + Sequence< Sequence< OUString > > aComplexLabels(nNumSequences); + auto aComplexLabelsRange = asNonConstRange(aComplexLabels); + for( const auto& rDataSequence : aSequencesToExport ) + { + OUString aRange; + Sequence< OUString >& rCurrentComplexLabel = aComplexLabelsRange[nSeqIdx]; + if( rDataSequence.first.is()) + { + lcl_getLabelStringSequence( rCurrentComplexLabel, rDataSequence.first ); + rLabels[nSeqIdx] = lcl_flattenStringSequence( rCurrentComplexLabel ); + aRange = rDataSequence.first->getSourceRangeRepresentation(); + if( xRangeConversion.is()) + aRange = xRangeConversion->convertRangeToXML( aRange ); + } + else if( rDataSequence.second.is()) + { + rCurrentComplexLabel.realloc(1); + rLabels[nSeqIdx] = rCurrentComplexLabel.getArray()[0] = lcl_flattenStringSequence( + rDataSequence.second->generateLabel( chart2::data::LabelOrigin_SHORT_SIDE )); + } + if( bSeriesFromColumns ) + aResult.aColumnDescriptions_Ranges.push_back( aRange ); + else + aResult.aRowDescriptions_Ranges.push_back( aRange ); + + ::std::vector< double > aNumbers( lcl_getAllValuesFromSequence( rDataSequence.second )); + if( bSeriesFromColumns ) + { + const sal_Int32 nSize( static_cast< sal_Int32 >( aNumbers.size())); + for( sal_Int32 nIdx=0; nIdx<nSize; ++nIdx ) + aResult.aDataInRows[nIdx][nSeqIdx] = aNumbers[nIdx]; + } + else + aResult.aDataInRows[nSeqIdx] = aNumbers; + + if( rDataSequence.second.is()) + { + aRange = rDataSequence.second->getSourceRangeRepresentation(); + if( xRangeConversion.is()) + aRange = xRangeConversion->convertRangeToXML( aRange ); + } + aResult.aDataRangeRepresentations.push_back( aRange ); + + //is column hidden? + if( !lcl_SequenceHasUnhiddenData(rDataSequence.first) && !lcl_SequenceHasUnhiddenData(rDataSequence.second) ) + aResult.aHiddenColumns.push_back(nSeqIdx); + + ++nSeqIdx; + } + Sequence< Sequence< Any > >& rComplexAnyLabels = bSeriesFromColumns ? aResult.aComplexColumnDescriptions : aResult.aComplexRowDescriptions;//#i116544# + rComplexAnyLabels.realloc(aComplexLabels.getLength()); + auto pComplexAnyLabels = rComplexAnyLabels.getArray(); + for( sal_Int32 nN=0; nN<aComplexLabels.getLength();nN++ ) + { + Sequence< OUString >& rSource = aComplexLabelsRange[nN]; + Sequence< Any >& rTarget = pComplexAnyLabels[nN]; + rTarget.realloc( rSource.getLength() ); + auto pTarget = rTarget.getArray(); + for( sal_Int32 i=0; i<rSource.getLength(); i++ ) + pTarget[i] <<= rSource[i]; + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "something went wrong during table data collection"); + } + + return aResult; +} + +void lcl_exportNumberFormat( const OUString& rPropertyName, const Reference< beans::XPropertySet >& xPropSet, + SvXMLExport& rExport ) +{ + if( xPropSet.is()) + { + sal_Int32 nNumberFormat = 0; + Any aNumAny = xPropSet->getPropertyValue( rPropertyName ); + if( (aNumAny >>= nNumberFormat) && (nNumberFormat != -1) ) + rExport.addDataStyle( nNumberFormat ); + } +} + +::std::vector< Reference< chart2::data::XDataSequence > > + lcl_getErrorBarSequences( const Reference< beans::XPropertySet > & xErrorBarProp ) +{ + ::std::vector< Reference< chart2::data::XDataSequence > > aResult; + Reference< chart2::data::XDataSource > xErrorBarDataSource( xErrorBarProp, uno::UNO_QUERY ); + if( !xErrorBarDataSource.is()) + return aResult; + + const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( + xErrorBarDataSource->getDataSequences()); + for( const auto& rSequence : aSequences ) + { + try + { + if( rSequence.is()) + { + Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues()); + Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW ); + OUString aRole; + if( ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) && + aRole.match( "error-bars-" )) + { + aResult.push_back( xSequence ); + } + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "chart:exporting error bar ranges" ); + } + } + + return aResult; +} + +bool lcl_exportDomainForThisSequence( const Reference< chart2::data::XDataSequence >& rValues, OUString& rFirstRangeForThisDomainIndex, SvXMLExport& rExport ) +{ + bool bDomainExported = false; + if( rValues.is()) + { + Reference< chart2::XChartDocument > xNewDoc( rExport.GetModel(), uno::UNO_QUERY ); + OUString aRange( lcl_ConvertRange( rValues->getSourceRangeRepresentation(), xNewDoc ) ); + + //work around error in OOo 2.0 (problems with multiple series having a domain element) + if( rFirstRangeForThisDomainIndex.isEmpty() || aRange != rFirstRangeForThisDomainIndex ) + { + rExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, aRange); + SvXMLElementExport aDomain( rExport, XML_NAMESPACE_CHART, XML_DOMAIN, true, true ); + bDomainExported = true; + } + + if( rFirstRangeForThisDomainIndex.isEmpty() ) + rFirstRangeForThisDomainIndex = aRange; + } + return bDomainExported; +} + +} // anonymous namespace + + +SchXMLExportHelper::SchXMLExportHelper( SvXMLExport& rExport, SvXMLAutoStylePoolP& rASPool ) + : m_pImpl( new SchXMLExportHelper_Impl( rExport, rASPool ) ) +{ +} + +SchXMLExportHelper::~SchXMLExportHelper() +{ +} + +const OUString& SchXMLExportHelper::getChartCLSID() const +{ + return m_pImpl->msCLSID; +} + +void SchXMLExportHelper::SetSourceShellID( const OUString& rShellID ) +{ + m_pImpl->maSrcShellID = rShellID; +} + +void SchXMLExportHelper::SetDestinationShellID( const OUString& rShellID ) +{ + m_pImpl->maDestShellID = rShellID; +} + +const rtl::Reference< XMLPropertySetMapper >& SchXMLExportHelper_Impl::GetPropertySetMapper() const +{ + return mxPropertySetMapper; +} + +void SchXMLExportHelper_Impl::exportAutoStyles() +{ + if( !mxExpPropMapper.is()) + return; + + //ToDo: when embedded in calc/writer this is not necessary because the + // numberformatter is shared between both documents + mrExport.exportAutoDataStyles(); + + // export chart auto styles + mrAutoStylePool.exportXML( XmlStyleFamily::SCH_CHART_ID ); + + // export auto styles for additional shapes + mrExport.GetShapeExport()->exportAutoStyles(); + // and for text in additional shapes + mrExport.GetTextParagraphExport()->exportTextAutoStyles(); +} + +// private methods + +SchXMLExportHelper_Impl::SchXMLExportHelper_Impl( + SvXMLExport& rExport, + SvXMLAutoStylePoolP& rASPool ) : + mrExport( rExport ), + mrAutoStylePool( rASPool ), + mxPropertySetMapper( new XMLChartPropertySetMapper(&rExport) ), + mxExpPropMapper( new XMLChartExportPropertyMapper( mxPropertySetMapper, rExport ) ), + mbHasCategoryLabels( false ), + mbRowSourceColumns( true ), + msCLSID( SvGlobalName( SO3_SCH_CLASSID ).GetHexName() ) +{ + // register chart auto-style family + mrAutoStylePool.AddFamily( + XmlStyleFamily::SCH_CHART_ID, + OUString( XML_STYLE_FAMILY_SCH_CHART_NAME ), + mxExpPropMapper.get(), + OUString( XML_STYLE_FAMILY_SCH_CHART_PREFIX )); + + // register shape family + mrAutoStylePool.AddFamily( + XmlStyleFamily::SD_GRAPHICS_ID, + OUString( XML_STYLE_FAMILY_SD_GRAPHICS_NAME ), + mxExpPropMapper.get(), + OUString( XML_STYLE_FAMILY_SD_GRAPHICS_PREFIX )); + // register paragraph family also for shapes + mrAutoStylePool.AddFamily( + XmlStyleFamily::TEXT_PARAGRAPH, + GetXMLToken( XML_PARAGRAPH ), + mxExpPropMapper.get(), + OUString( 'P' )); + // register text family also for shapes + mrAutoStylePool.AddFamily( + XmlStyleFamily::TEXT_TEXT, + GetXMLToken( XML_TEXT ), + mxExpPropMapper.get(), + OUString( 'T' )); +} + +void SchXMLExportHelper_Impl::collectAutoStyles( Reference< chart::XChartDocument > const & rChartDoc ) +{ + parseDocument( rChartDoc, false ); +} + +void SchXMLExportHelper_Impl::exportChart( Reference< chart::XChartDocument > const & rChartDoc, + bool bIncludeTable ) +{ + parseDocument( rChartDoc, true, bIncludeTable ); + SAL_WARN_IF( !maAutoStyleNameQueue.empty(), "xmloff.chart", "There are still remaining autostyle names in the queue" ); +} + +static OUString lcl_GetStringFromNumberSequence( const css::uno::Sequence< sal_Int32 >& rSequenceMapping, bool bRemoveOneFromEachIndex /*should be true if having categories*/ ) +{ + OUStringBuffer aBuf; + bool bHasPredecessor = false; + for( sal_Int32 nIndex : rSequenceMapping ) + { + if( bRemoveOneFromEachIndex ) + --nIndex; + if(nIndex>=0) + { + if(bHasPredecessor) + aBuf.append( ' ' ); + aBuf.append( nIndex ); + bHasPredecessor = true; + } + } + return aBuf.makeStringAndClear(); +} + +/// if bExportContent is false the auto-styles are collected +void SchXMLExportHelper_Impl::parseDocument( Reference< chart::XChartDocument > const & rChartDoc, + bool bExportContent, + bool bIncludeTable ) +{ + Reference< chart2::XChartDocument > xNewDoc( rChartDoc, uno::UNO_QUERY ); + if( !rChartDoc.is() || !xNewDoc.is() ) + { + SAL_WARN("xmloff.chart", "No XChartDocument was given for export." ); + return; + } + + mxExpPropMapper->setChartDoc(xNewDoc); + + awt::Size aPageSize( getPageSize( xNewDoc )); + if( bExportContent ) + addSize( aPageSize ); + Reference< chart::XDiagram > xDiagram = rChartDoc->getDiagram(); + Reference< chart2::XDiagram > xNewDiagram; + if( xNewDoc.is()) + xNewDiagram.set( xNewDoc->getFirstDiagram()); + + //todo remove if model changes are notified and view is updated automatically + if( bExportContent ) + { + Reference< util::XRefreshable > xRefreshable( xNewDoc, uno::UNO_QUERY ); + if( xRefreshable.is() ) + xRefreshable->refresh(); + } + + // get Properties of ChartDocument + bool bHasMainTitle = false; + bool bHasSubTitle = false; + bool bHasLegend = false; + util::DateTime aNullDate(0,0,0,0,30,12,1899, false); + + std::vector< XMLPropertyState > aPropertyStates; + + Reference< beans::XPropertySet > xDocPropSet( rChartDoc, uno::UNO_QUERY ); + if( xDocPropSet.is()) + { + try + { + Any aAny = xDocPropSet->getPropertyValue("HasMainTitle"); + aAny >>= bHasMainTitle; + aAny = xDocPropSet->getPropertyValue("HasSubTitle"); + aAny >>= bHasSubTitle; + aAny = xDocPropSet->getPropertyValue("HasLegend"); + aAny >>= bHasLegend; + if ( bIncludeTable ) + { + aAny = xDocPropSet->getPropertyValue("NullDate"); + if ( !aAny.hasValue() ) + { + Reference<container::XChild> xChild(rChartDoc, uno::UNO_QUERY ); + if ( xChild.is() ) + { + Reference< beans::XPropertySet > xParentDoc( xChild->getParent(),uno::UNO_QUERY); + if ( xParentDoc.is() && xParentDoc->getPropertySetInfo()->hasPropertyByName("NullDate") ) + aAny = xParentDoc->getPropertyValue("NullDate"); + } + } + + aAny >>= aNullDate; + } + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Required property not found in ChartDocument" ); + } + } + + if ( bIncludeTable && (aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899 ) ) + { + SvXMLElementExport aSet( mrExport, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true ); + { + OUStringBuffer sBuffer; + ::sax::Converter::convertDateTime(sBuffer, aNullDate, nullptr); + mrExport.AddAttribute( XML_NAMESPACE_TABLE,XML_DATE_VALUE,sBuffer.makeStringAndClear()); + SvXMLElementExport aNull( mrExport, XML_NAMESPACE_TABLE, XML_NULL_DATE, true, true ); + } + } + + // chart element + std::unique_ptr<SvXMLElementExport> xElChart; + + // get property states for autostyles + if( mxExpPropMapper.is()) + { + Reference< beans::XPropertySet > xPropSet = rChartDoc->getArea(); + if( xPropSet.is()) + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + } + + if( bExportContent ) + { + //export data provider in xlink:href attribute + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012) + { + OUString aDataProviderURL( ".." ); + if( xNewDoc->hasInternalDataProvider() ) + aDataProviderURL = "."; + else //special handling for data base data provider necessary + { + Reference< chart2::data::XDatabaseDataProvider > xDBDataProvider( xNewDoc->getDataProvider(), uno::UNO_QUERY ); + if( xDBDataProvider.is() ) + aDataProviderURL = "."; + } + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, aDataProviderURL ); + mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE ); + } + + Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xNewDoc->getDataProvider(), uno::UNO_QUERY); + if (xPivotTableDataProvider.is() && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) + { + OUString sPivotTableName = xPivotTableDataProvider->getPivotTableName(); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_PILOT_SOURCE, sPivotTableName); + } + + OUString sChartType( xDiagram->getDiagramType() ); + + // attributes + // determine class + if( !sChartType.isEmpty()) + { + enum XMLTokenEnum eXMLChartType = SchXMLTools::getTokenByChartType( sChartType, true /* bUseOldNames */ ); + + SAL_WARN_IF( eXMLChartType == XML_TOKEN_INVALID, "xmloff.chart", "invalid chart class" ); + if( eXMLChartType == XML_TOKEN_INVALID ) + eXMLChartType = XML_BAR; + + if( eXMLChartType == XML_ADD_IN ) + { + // sChartType is the service-name of the add-in + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_OOO, sChartType) ); + } + else if( eXMLChartType != XML_TOKEN_INVALID ) + { + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_CHART, GetXMLToken(eXMLChartType )) ); + } + + //column-mapping or row-mapping + if( maSequenceMapping.hasElements() ) + { + enum XMLTokenEnum eTransToken = ::xmloff::token::XML_ROW_MAPPING; + if( mbRowSourceColumns ) + eTransToken = ::xmloff::token::XML_COLUMN_MAPPING; + OUString aSequenceMappingStr( lcl_GetStringFromNumberSequence( + maSequenceMapping, mbHasCategoryLabels && !xNewDoc->hasInternalDataProvider() ) ); + + mrExport.AddAttribute( XML_NAMESPACE_CHART, + ::xmloff::token::GetXMLToken( eTransToken ), + aSequenceMappingStr ); + } + } + // write style name + AddAutoStyleAttribute( aPropertyStates ); + + //element + xElChart.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_CHART, true, true )); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + // remove property states for autostyles + aPropertyStates.clear(); + + // title element + if( bHasMainTitle ) + { + // get property states for autostyles + if( mxExpPropMapper.is()) + { + Reference< beans::XPropertySet > xPropSet( rChartDoc->getTitle(), uno::UNO_QUERY ); + if( xPropSet.is()) + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + } + if( bExportContent ) + { + Reference< drawing::XShape > xShape = rChartDoc->getTitle(); + if( xShape.is()) // && "hasTitleBeenMoved" + addPosition( xShape ); + + // write style name + AddAutoStyleAttribute( aPropertyStates ); + + // element + SvXMLElementExport aElTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, true, true ); + + // content (text:p) + Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); + if( xPropSet.is()) + { + Any aAny( xPropSet->getPropertyValue( "String" )); + OUString aText; + aAny >>= aText; + exportText( aText ); + } + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + // remove property states for autostyles + aPropertyStates.clear(); + } + + // subtitle element + if( bHasSubTitle ) + { + // get property states for autostyles + if( mxExpPropMapper.is()) + { + Reference< beans::XPropertySet > xPropSet( rChartDoc->getSubTitle(), uno::UNO_QUERY ); + if( xPropSet.is()) + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + } + + if( bExportContent ) + { + Reference< drawing::XShape > xShape = rChartDoc->getSubTitle(); + if( xShape.is()) + addPosition( xShape ); + + // write style name + AddAutoStyleAttribute( aPropertyStates ); + + // element (has no subelements) + SvXMLElementExport aElSubTitle( mrExport, XML_NAMESPACE_CHART, XML_SUBTITLE, true, true ); + + // content (text:p) + Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); + if( xPropSet.is()) + { + Any aAny( xPropSet->getPropertyValue( "String" )); + OUString aText; + aAny >>= aText; + exportText( aText ); + } + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + // remove property states for autostyles + aPropertyStates.clear(); + } + + // legend element + if( bHasLegend ) + { + // get property states for autostyles + if( mxExpPropMapper.is()) + { + Reference< beans::XPropertySet > xPropSet( rChartDoc->getLegend(), uno::UNO_QUERY ); + if( xPropSet.is()) + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + } + + if( bExportContent ) + { + Reference< beans::XPropertySet > xProp( rChartDoc->getLegend(), uno::UNO_QUERY ); + if( xProp.is()) + { + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + + // export legend anchor position + try + { + Any aAny( xProp->getPropertyValue("Alignment")); + if( SchXMLEnumConverter::getLegendPositionConverter().exportXML( msString, aAny, mrExport.GetMM100UnitConverter() ) ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LEGEND_POSITION, msString ); + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Property Align not found in ChartLegend" ); + } + + // export legend overlay + try + { + if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) + { + Any aAny( xProp->getPropertyValue("Overlay")); + if(aAny.get<bool>()) + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_OVERLAY, OUString::boolean(true)); + } + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Property Overlay not found in ChartLegend" ); + } + + // export absolute legend position + Reference< drawing::XShape > xLegendShape( xProp, uno::UNO_QUERY ); + addPosition( xLegendShape ); + + // export legend size + if (xLegendShape.is() && nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012) + { + try + { + chart::ChartLegendExpansion nLegendExpansion = chart::ChartLegendExpansion_HIGH; + OUString aExpansionString; + Any aAny( xProp->getPropertyValue("Expansion")); + bool bHasExpansion = (aAny >>= nLegendExpansion); + if( bHasExpansion && SchXMLEnumConverter::getLegendExpansionConverter().exportXML( aExpansionString, aAny, mrExport.GetMM100UnitConverter() ) ) + { + mrExport.AddAttribute( XML_NAMESPACE_STYLE, XML_LEGEND_EXPANSION, aExpansionString ); + if( nLegendExpansion == chart::ChartLegendExpansion_CUSTOM) + { + awt::Size aSize( xLegendShape->getSize() ); + // tdf#131966: chart legend attributes width and height shouldn't be exported to ODF 1.2 (strict) + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_013) + { // ODF 1.3 OFFICE-3883 + addSize( aSize, false ); + } + else if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) + { + addSize( aSize, true ); + } + OUStringBuffer aAspectRatioString; + ::sax::Converter::convertDouble( + aAspectRatioString, + (aSize.Height == 0 + ? 1.0 + : double(aSize.Width)/double(aSize.Height))); + mrExport.AddAttribute( XML_NAMESPACE_STYLE, XML_LEGEND_EXPANSION_ASPECT_RATIO, aAspectRatioString.makeStringAndClear() ); + } + } + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Property Expansion not found in ChartLegend" ); + } + } + } + + // write style name + AddAutoStyleAttribute( aPropertyStates ); + + // element + SvXMLElementExport aLegend( mrExport, XML_NAMESPACE_CHART, XML_LEGEND, true, true ); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + // remove property states for autostyles + aPropertyStates.clear(); + } + + // plot-area element + if( xDiagram.is()) + exportPlotArea( xDiagram, xNewDiagram, aPageSize, bExportContent, bIncludeTable ); + + // export additional shapes + if( xDocPropSet.is() ) + { + if( bExportContent ) + { + if( mxAdditionalShapes.is()) + { + // can't call exportShapes with all shapes because the + // initialisation happened with the complete draw page and not + // the XShapes object used here. Thus the shapes have to be + // exported one by one + rtl::Reference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport(); + Reference< drawing::XShape > xShape; + const sal_Int32 nShapeCount( mxAdditionalShapes->getCount()); + for( sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++ ) + { + mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape; + SAL_WARN_IF( !xShape.is(), "xmloff.chart", "Shape without an XShape?" ); + if( ! xShape.is()) + continue; + + rShapeExport->exportShape( xShape ); + } + // this would be the easier way if it worked: + //mrExport.GetShapeExport()->exportShapes( mxAdditionalShapes ); + } + } + else + { + // get a sequence of non-chart shapes (inserted via clipboard) + try + { + Any aShapesAny = xDocPropSet->getPropertyValue("AdditionalShapes"); + aShapesAny >>= mxAdditionalShapes; + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "AdditionalShapes not found" ); + } + + if( mxAdditionalShapes.is()) + { + // seek shapes has to be called for the whole page because in + // the shape export the vector of shapes is accessed via the + // ZOrder which might be (actually is) larger than the number of + // shapes in mxAdditionalShapes + Reference< drawing::XDrawPageSupplier > xSupplier( rChartDoc, uno::UNO_QUERY ); + SAL_WARN_IF( !xSupplier.is(), "xmloff.chart", "Cannot retrieve draw page to initialize shape export" ); + if( xSupplier.is() ) + { + Reference< drawing::XShapes > xDrawPage = xSupplier->getDrawPage(); + SAL_WARN_IF( !xDrawPage.is(), "xmloff.chart", "Invalid draw page for initializing shape export" ); + if( xDrawPage.is()) + mrExport.GetShapeExport()->seekShapes( xDrawPage ); + } + + // can't call collectShapesAutoStyles with all shapes because + // the initialisation happened with the complete draw page and + // not the XShapes object used here. Thus the shapes have to be + // exported one by one + rtl::Reference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport(); + Reference< drawing::XShape > xShape; + const sal_Int32 nShapeCount( mxAdditionalShapes->getCount()); + for( sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++ ) + { + mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape; + SAL_WARN_IF( !xShape.is(), "xmloff.chart", "Shape without an XShape?" ); + if( ! xShape.is()) + continue; + + rShapeExport->collectShapeAutoStyles( xShape ); + } + } + } + } + + // table element + // (is included as subelement of chart) + if( bExportContent ) + { + // #85929# always export table, otherwise clipboard may lose data + exportTable(); + } +} + +static void lcl_exportComplexLabel( const Sequence< uno::Any >& rComplexLabel, SvXMLExport& rExport ) +{ + sal_Int32 nLength = rComplexLabel.getLength(); + if( nLength<=1 ) + return; + SvXMLElementExport aTextList( rExport, XML_NAMESPACE_TEXT, XML_LIST, true, true ); + for(const auto& rElem : rComplexLabel) + { + SvXMLElementExport aListItem( rExport, XML_NAMESPACE_TEXT, XML_LIST_ITEM, true, true ); + OUString aString; + if( !(rElem >>= aString) ) + { + double aNum; + if (rElem >>= aNum) + { + aString = OUString::number(aNum); + } + } + SchXMLTools::exportText( rExport, aString, false /*bConvertTabsLFs*/ ); + } +} + +void SchXMLExportHelper_Impl::exportTable() +{ + // table element + mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, gsTableName ); + + try + { + bool bProtected = false; + Reference< beans::XPropertySet > xProps( mrExport.GetModel(), uno::UNO_QUERY_THROW ); + if ( ( xProps->getPropertyValue("DisableDataTableDialog") >>= bProtected ) && + bProtected ) + { + mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE ); + } + } + catch ( const uno::Exception& ) + { + } + + SvXMLElementExport aTable( mrExport, XML_NAMESPACE_TABLE, XML_TABLE, true, true ); + + bool bHasOwnData = false; + Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY ); + Reference< chart2::data::XRangeXMLConversion > xRangeConversion; + if( xNewDoc.is()) + { + bHasOwnData = xNewDoc->hasInternalDataProvider(); + xRangeConversion.set( xNewDoc->getDataProvider(), uno::UNO_QUERY ); + } + + Reference< chart2::XAnyDescriptionAccess > xAnyDescriptionAccess; + { + Reference< chart::XChartDocument > xChartDoc( mrExport.GetModel(), uno::UNO_QUERY ); + if( xChartDoc.is() ) + xAnyDescriptionAccess.set( xChartDoc->getData(), uno::UNO_QUERY ); + } + + if( bHasOwnData ) + lcl_ReorderInternalSequencesAccordingToTheirRangeName( m_aDataSequencesToExport ); + lcl_TableData aData( lcl_getDataForLocalTable( m_aDataSequencesToExport + , xAnyDescriptionAccess, maCategoriesRange + , mbRowSourceColumns, xRangeConversion )); + + tStringVector::const_iterator aDataRangeIter( aData.aDataRangeRepresentations.begin()); + const tStringVector::const_iterator aDataRangeEndIter( aData.aDataRangeRepresentations.end()); + + tStringVector::const_iterator aRowDescriptions_RangeIter( aData.aRowDescriptions_Ranges.begin()); + const tStringVector::const_iterator aRowDescriptions_RangeEnd( aData.aRowDescriptions_Ranges.end()); + + // declare columns + { + SvXMLElementExport aHeaderColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true, true ); + SvXMLElementExport aHeaderColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true ); + } + { + SvXMLElementExport aColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMNS, true, true ); + + sal_Int32 nNextIndex = 0; + for(sal_Int32 nHiddenIndex : aData.aHiddenColumns) + { + //i91578 display of hidden values (copy paste scenario; export hidden flag thus it can be used during migration to locale table upon paste ) + if( nHiddenIndex > nNextIndex ) + { + sal_Int64 nRepeat = static_cast< sal_Int64 >( nHiddenIndex - nNextIndex ); + if(nRepeat>1) + mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, + OUString::number( nRepeat )); + SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true ); + } + mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VISIBILITY, GetXMLToken( XML_COLLAPSE ) ); + SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true ); + nNextIndex = nHiddenIndex+1; + } + + sal_Int32 nEndIndex = aData.aColumnDescriptions.size()-1; + if( nEndIndex >= nNextIndex ) + { + sal_Int64 nRepeat = static_cast< sal_Int64 >( nEndIndex - nNextIndex + 1 ); + if(nRepeat>1) + mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, + OUString::number( nRepeat )); + SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true ); + } + } + + // export rows with content + //export header row + { + SvXMLElementExport aHeaderRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true, true ); + SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true ); + + //first one empty cell for the row descriptions + { + SvXMLElementExport aEmptyCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true ); + SvXMLElementExport aEmptyParagraph( mrExport, XML_NAMESPACE_TEXT, XML_P, true, true ); + } + + //export column descriptions + tStringVector::const_iterator aColumnDescriptions_RangeIter( aData.aColumnDescriptions_Ranges.begin()); + const tStringVector::const_iterator aColumnDescriptions_RangeEnd( aData.aColumnDescriptions_Ranges.end()); + const Sequence< Sequence< uno::Any > >& rComplexColumnDescriptions = aData.aComplexColumnDescriptions; + sal_Int32 nComplexCount = rComplexColumnDescriptions.getLength(); + sal_Int32 nC = 0; + for( const auto& rDesc : aData.aColumnDescriptions ) + { + bool bExportString = true; + if( nC < nComplexCount ) + { + const Sequence< uno::Any >& rComplexLabel = rComplexColumnDescriptions[nC]; + if( rComplexLabel.hasElements() ) + { + double fValue=0.0; + if( rComplexLabel[0] >>=fValue ) + { + bExportString = false; + + ::sax::Converter::convertDouble( + msStringBuffer, fValue); + msString = msStringBuffer.makeStringAndClear(); + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT ); + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString ); + } + } + } + if( bExportString ) + { + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING ); + } + + SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true ); + exportText( rDesc ); + if( nC < nComplexCount ) + lcl_exportComplexLabel( rComplexColumnDescriptions[nC], mrExport ); + if( !bHasOwnData && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd ) + { + // remind the original range to allow a correct re-association when copying via clipboard + if (!(*aColumnDescriptions_RangeIter).isEmpty()) + SchXMLTools::exportRangeToSomewhere( mrExport, *aColumnDescriptions_RangeIter ); + ++aColumnDescriptions_RangeIter; + } + + nC++; + } + SAL_WARN_IF( !bHasOwnData && (aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd), "xmloff.chart", "bHasOwnData == false && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd" ); + } // closing row and header-rows elements + + // export value rows + { + SvXMLElementExport aRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROWS, true, true ); + tStringVector::const_iterator aRowDescriptionsIter( aData.aRowDescriptions.begin()); + const Sequence< Sequence< uno::Any > >& rComplexRowDescriptions = aData.aComplexRowDescriptions; + sal_Int32 nComplexCount = rComplexRowDescriptions.getLength(); + sal_Int32 nC = 0; + + for( const auto& rRow : aData.aDataInRows ) + { + SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true ); + + //export row descriptions + { + bool bExportString = true; + if( nC < nComplexCount ) + { + const Sequence< uno::Any >& rComplexLabel = rComplexRowDescriptions[nC]; + if( rComplexLabel.hasElements() ) + { + double fValue=0.0; + if( rComplexLabel[0] >>=fValue ) + { + bExportString = false; + + ::sax::Converter::convertDouble(msStringBuffer, fValue); + msString = msStringBuffer.makeStringAndClear(); + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT ); + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString ); + } + } + } + if( bExportString ) + { + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING ); + } + + SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true ); + if( aRowDescriptionsIter != aData.aRowDescriptions.end()) + { + exportText( *aRowDescriptionsIter ); + if( nC < nComplexCount ) + lcl_exportComplexLabel( rComplexRowDescriptions[nC], mrExport ); + if( !bHasOwnData && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd ) + { + // remind the original range to allow a correct re-association when copying via clipboard + SchXMLTools::exportRangeToSomewhere( mrExport, *aRowDescriptions_RangeIter ); + ++aRowDescriptions_RangeIter; + } + ++aRowDescriptionsIter; + } + } + + //export row values + for( t2DNumberContainer::value_type::const_iterator aColIt( rRow.begin()); + aColIt != rRow.end(); ++aColIt ) + { + ::sax::Converter::convertDouble( msStringBuffer, *aColIt ); + msString = msStringBuffer.makeStringAndClear(); + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT ); + mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString ); + SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true ); + exportText( msString ); // do not convert tabs and lfs + if( ( !bHasOwnData && aDataRangeIter != aDataRangeEndIter ) && + ( mbRowSourceColumns || (aColIt == rRow.begin()) ) ) + { + // remind the original range to allow a correct re-association when copying via clipboard + if (!(*aDataRangeIter).isEmpty()) + SchXMLTools::exportRangeToSomewhere( mrExport, *aDataRangeIter ); + ++aDataRangeIter; + } + } + + ++nC; + } + } + + // if range iterator was used it should have reached its end + SAL_WARN_IF( !bHasOwnData && (aDataRangeIter != aDataRangeEndIter), "xmloff.chart", "bHasOwnData == false && aDataRangeIter != aDataRangeEndIter" ); + SAL_WARN_IF( !bHasOwnData && (aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd), "xmloff.chart", "bHasOwnData == false && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd" ); +} + +namespace +{ + +Reference< chart2::XCoordinateSystem > lcl_getCooSys( const Reference< chart2::XDiagram > & xNewDiagram ) +{ + Reference< chart2::XCoordinateSystem > xCooSys; + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDiagram, uno::UNO_QUERY ); + if(xCooSysCnt.is()) + { + Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() ); + if(aCooSysSeq.hasElements()) + xCooSys = aCooSysSeq[0]; + } + return xCooSys; +} + +Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem >& xCooSys, + enum XMLTokenEnum eDimension, bool bPrimary=true ) +{ + Reference< chart2::XAxis > xNewAxis; + try + { + if( xCooSys.is() ) + { + sal_Int32 nDimensionIndex=0; + switch( eDimension ) + { + case XML_X: + nDimensionIndex=0; + break; + case XML_Y: + nDimensionIndex=1; + break; + case XML_Z: + nDimensionIndex=2; + break; + default: + break; + } + + xNewAxis = xCooSys->getAxisByDimension( nDimensionIndex, bPrimary ? 0 : 1 ); + } + } + catch( const uno::Exception & ) + { + } + return xNewAxis; +} + +} + +void SchXMLExportHelper_Impl::exportPlotArea( + const Reference< chart::XDiagram >& xDiagram, + const Reference< chart2::XDiagram >& xNewDiagram, + const awt::Size & rPageSize, + bool bExportContent, + bool bIncludeTable ) +{ + SAL_WARN_IF( !xDiagram.is(), "xmloff.chart", "Invalid XDiagram as parameter" ); + if( ! xDiagram.is()) + return; + + // variables for autostyles + Reference< beans::XPropertySet > xPropSet; + std::vector< XMLPropertyState > aPropertyStates; + + msStringBuffer.setLength( 0 ); + + // plot-area element + + std::unique_ptr<SvXMLElementExport> xElPlotArea; + // get property states for autostyles + xPropSet.set( xDiagram, uno::UNO_QUERY ); + if( xPropSet.is()) + { + if( mxExpPropMapper.is()) + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + } + if( bExportContent ) + { + rtl::Reference< XMLShapeExport > rShapeExport; + + // write style name + AddAutoStyleAttribute( aPropertyStates ); + + if( !msChartAddress.isEmpty() ) + { + if( !bIncludeTable ) + mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, msChartAddress ); + + Reference< chart::XChartDocument > xDoc( mrExport.GetModel(), uno::UNO_QUERY ); + if( xDoc.is() ) + { + Reference< beans::XPropertySet > xDocProp( xDoc, uno::UNO_QUERY ); + if( xDocProp.is()) + { + Any aAny; + + try + { + bool bFirstCol = false, bFirstRow = false; + + aAny = xDocProp->getPropertyValue( "DataSourceLabelsInFirstColumn" ); + aAny >>= bFirstCol; + aAny = xDocProp->getPropertyValue( "DataSourceLabelsInFirstRow" ); + aAny >>= bFirstRow; + + if( bFirstCol || bFirstRow ) + { + mrExport.AddAttribute( XML_NAMESPACE_CHART, + ::xmloff::token::GetXMLToken( ::xmloff::token::XML_DATA_SOURCE_HAS_LABELS ), + ( bFirstCol + ? ( bFirstRow + ? ::xmloff::token::GetXMLToken( ::xmloff::token::XML_BOTH ) + : ::xmloff::token::GetXMLToken( ::xmloff::token::XML_COLUMN )) + : ::xmloff::token::GetXMLToken( ::xmloff::token::XML_ROW ))); + } + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Properties missing" ); + } + } + } + } + + // attributes + if( xDiagram.is()) + { + addPosition( xDiagram ); + addSize( xDiagram ); + } + + bool bIs3DChart = false; + + if( xPropSet.is()) + { + Any aAny; + + // 3d attributes + try + { + aAny = xPropSet->getPropertyValue("Dim3D"); + aAny >>= bIs3DChart; + + if( bIs3DChart ) + { + rShapeExport = mrExport.GetShapeExport(); + if( rShapeExport.is()) + rShapeExport->export3DSceneAttributes( xPropSet ); + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "chart:exportPlotAreaException caught"); + } + } + + // plot-area element + xElPlotArea.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_PLOT_AREA, true, true )); + + //inner position rectangle element + exportCoordinateRegion( xDiagram ); + + // light sources (inside plot area element) + if( bIs3DChart && + rShapeExport.is()) + rShapeExport->export3DLamps( xPropSet ); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + // remove property states for autostyles + aPropertyStates.clear(); + + // axis elements + exportAxes( xDiagram, xNewDiagram, bExportContent ); + + // series elements + Reference< chart2::XAxis > xSecondYAxis = lcl_getAxis( lcl_getCooSys( xNewDiagram ), XML_Y, false ); + exportSeries( xNewDiagram, rPageSize, bExportContent, xSecondYAxis.is() ); + + // stock-chart elements + OUString sChartType ( xDiagram->getDiagramType()); + if( sChartType == "com.sun.star.chart.StockDiagram" ) + { + Reference< chart::XStatisticDisplay > xStockPropProvider( xDiagram, uno::UNO_QUERY ); + if( xStockPropProvider.is()) + { + // stock-gain-marker + Reference< beans::XPropertySet > xStockPropSet = xStockPropProvider->getUpBar(); + if( xStockPropSet.is()) + { + aPropertyStates.clear(); + aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet); + + if( !aPropertyStates.empty() ) + { + if( bExportContent ) + { + AddAutoStyleAttribute( aPropertyStates ); + + SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_GAIN_MARKER, true, true ); + } + else + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + } + } + + // stock-loss-marker + xStockPropSet = xStockPropProvider->getDownBar(); + if( xStockPropSet.is()) + { + aPropertyStates.clear(); + aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet); + + if( !aPropertyStates.empty() ) + { + if( bExportContent ) + { + AddAutoStyleAttribute( aPropertyStates ); + + SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_LOSS_MARKER, true, true ); + } + else + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + } + } + + // stock-range-line + xStockPropSet = xStockPropProvider->getMinMaxLine(); + if( xStockPropSet.is()) + { + aPropertyStates.clear(); + aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet); + + if( !aPropertyStates.empty() ) + { + if( bExportContent ) + { + AddAutoStyleAttribute( aPropertyStates ); + + SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_RANGE_LINE, true, true ); + } + else + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + } + } + } + } + + // wall and floor element + Reference< chart::X3DDisplay > xWallFloorSupplier( xDiagram, uno::UNO_QUERY ); + if( !(mxExpPropMapper.is() && + xWallFloorSupplier.is())) + return; + + // remove property states for autostyles + aPropertyStates.clear(); + + Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall(); + if( xWallPropSet.is()) + { + aPropertyStates = mxExpPropMapper->Filter(mrExport, xWallPropSet); + + if( !aPropertyStates.empty() ) + { + // write element + if( bExportContent ) + { + // add style name attribute + AddAutoStyleAttribute( aPropertyStates ); + + SvXMLElementExport aWall( mrExport, XML_NAMESPACE_CHART, XML_WALL, true, true ); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + } + } + + // floor element + // remove property states for autostyles + aPropertyStates.clear(); + + Reference< beans::XPropertySet > xFloorPropSet = xWallFloorSupplier->getFloor(); + if( !xFloorPropSet.is()) + return; + + aPropertyStates = mxExpPropMapper->Filter(mrExport, xFloorPropSet); + + if( aPropertyStates.empty() ) + return; + + // write element + if( bExportContent ) + { + // add style name attribute + AddAutoStyleAttribute( aPropertyStates ); + + SvXMLElementExport aFloor( mrExport, XML_NAMESPACE_CHART, XML_FLOOR, true, true ); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } +} + +void SchXMLExportHelper_Impl::exportCoordinateRegion( const uno::Reference< chart::XDiagram >& xDiagram ) +{ + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentODFVersion <= SvtSaveOptions::ODFSVER_012) //do not export to ODF 1.2 or older + return; + + Reference< chart::XDiagramPositioning > xDiaPos( xDiagram, uno::UNO_QUERY ); + SAL_WARN_IF( !xDiaPos.is(), "xmloff.chart", "Invalid xDiaPos as parameter" ); + if( !xDiaPos.is() ) + return; + + awt::Rectangle aRect( xDiaPos->calculateDiagramPositionExcludingAxes() ); + addPosition( awt::Point(aRect.X,aRect.Y) ); + addSize( awt::Size(aRect.Width,aRect.Height) ); + + // ODF 1.3 OFFICE-3928 + SvXMLElementExport aCoordinateRegion( mrExport, + (SvtSaveOptions::ODFSVER_013 <= nCurrentODFVersion) ? XML_NAMESPACE_CHART : XML_NAMESPACE_CHART_EXT, + XML_COORDINATE_REGION, true, true ); +} + +namespace +{ + XMLTokenEnum lcl_getTimeUnitToken( sal_Int32 nTimeUnit ) + { + XMLTokenEnum eToken = XML_DAYS; + switch( nTimeUnit ) + { + case css::chart::TimeUnit::YEAR: + eToken = XML_YEARS; + break; + case css::chart::TimeUnit::MONTH: + eToken = XML_MONTHS; + break; + default://days + break; + } + return eToken; + } +} + +void SchXMLExportHelper_Impl::exportDateScale( const Reference< beans::XPropertySet >& rAxisProps ) +{ + if( !rAxisProps.is() ) + return; + + chart::TimeIncrement aIncrement; + if( !(rAxisProps->getPropertyValue("TimeIncrement") >>= aIncrement) ) + return; + + sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY; + if( aIncrement.TimeResolution >>= nTimeResolution ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_BASE_TIME_UNIT, lcl_getTimeUnitToken( nTimeResolution ) ); + + chart::TimeInterval aInterval; + if( aIncrement.MajorTimeInterval >>= aInterval ) + { + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_VALUE, OUString::number(aInterval.Number) ); + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) ); + } + if( aIncrement.MinorTimeInterval >>= aInterval ) + { + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_VALUE, OUString::number(aInterval.Number) ); + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) ); + } + + SvXMLElementExport aDateScale( mrExport, XML_NAMESPACE_CHART_EXT, XML_DATE_SCALE, true, true );//#i25706#todo: change namespace for next ODF version +} + +void SchXMLExportHelper_Impl::exportAxisTitle( const Reference< beans::XPropertySet >& rTitleProps, bool bExportContent ) +{ + if( !rTitleProps.is() ) + return; + std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rTitleProps); + if( bExportContent ) + { + OUString aText; + Any aAny( rTitleProps->getPropertyValue( "String" )); + aAny >>= aText; + + Reference< drawing::XShape > xShape( rTitleProps, uno::UNO_QUERY ); + if( xShape.is()) + addPosition( xShape ); + + AddAutoStyleAttribute( aPropertyStates ); + SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, true, true ); + + // paragraph containing title + exportText( aText ); + } + else + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + aPropertyStates.clear(); +} + +void SchXMLExportHelper_Impl::exportGrid( const Reference< beans::XPropertySet >& rGridProperties, bool bMajor, bool bExportContent ) +{ + if( !rGridProperties.is() ) + return; + std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rGridProperties); + if( bExportContent ) + { + AddAutoStyleAttribute( aPropertyStates ); + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, bMajor ? XML_MAJOR : XML_MINOR ); + SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, true, true ); + } + else + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + aPropertyStates.clear(); +} + +namespace +{ + +//returns true if a date scale needs to be exported +bool lcl_exportAxisType( const Reference< chart2::XAxis >& rChart2Axis, SvXMLExport& rExport) +{ + bool bExportDateScale = false; + if( !rChart2Axis.is() ) + return bExportDateScale; + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + rExport.getSaneDefaultVersion()); + if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0) //do not export to ODF 1.3 or older + return bExportDateScale; + + chart2::ScaleData aScale( rChart2Axis->getScaleData() ); + //#i25706#todo: change namespace for next ODF version + sal_uInt16 nNameSpace = XML_NAMESPACE_CHART_EXT; + + switch(aScale.AxisType) + { + case chart2::AxisType::CATEGORY: + if( aScale.AutoDateAxis ) + { + rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO ); + bExportDateScale = true; + } + else + rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_TEXT ); + break; + case chart2::AxisType::DATE: + rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_DATE ); + bExportDateScale = true; + break; + default: //AUTOMATIC + rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO ); + break; + } + + return bExportDateScale; +} + +void disableLinkedNumberFormat( + std::vector<XMLPropertyState>& rPropStates, const rtl::Reference<XMLPropertySetMapper>& rMapper ) +{ + for (XMLPropertyState & rState : rPropStates) + { + if (rState.mnIndex < 0 || rMapper->GetEntryCount() <= rState.mnIndex) + continue; + + OUString aXMLName = rMapper->GetEntryXMLName(rState.mnIndex); + + if (aXMLName != "link-data-style-to-source") + continue; + + // Entry found. Set the value to false and bail out. + rState.maValue <<= false; + return; + } + + // Entry not found. Insert a new entry for this. + sal_Int32 nIndex = rMapper->GetEntryIndex(XML_NAMESPACE_CHART, u"link-data-style-to-source", 0); + XMLPropertyState aState(nIndex); + aState.maValue <<= false; + rPropStates.push_back(aState); +} + +} + +void SchXMLExportHelper_Impl::exportAxis( + enum XMLTokenEnum eDimension, + enum XMLTokenEnum eAxisName, + const Reference< beans::XPropertySet >& rAxisProps, + const Reference< chart2::XAxis >& rChart2Axis, + const OUString& rCategoriesRange, + bool bHasTitle, bool bHasMajorGrid, bool bHasMinorGrid, + bool bExportContent, std::u16string_view sChartType ) +{ + std::vector< XMLPropertyState > aPropertyStates; + std::unique_ptr<SvXMLElementExport> pAxis; + + // get property states for autostyles + if( rAxisProps.is() && mxExpPropMapper.is() ) + { + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED + && eDimension == XML_X) + { + chart2::ScaleData aScaleData(rChart2Axis->getScaleData()); + bool bShiftedCatPos = aScaleData.ShiftedCategoryPosition; + if (sChartType == u"com.sun.star.chart.BarDiagram" || sChartType == u"com.sun.star.chart.StockDiagram") + { + if (!bShiftedCatPos) + rAxisProps->setPropertyValue("MajorOrigin", uno::Any(0.0)); + } + else if (bShiftedCatPos) + rAxisProps->setPropertyValue("MajorOrigin", uno::Any(0.5)); + } + + lcl_exportNumberFormat( "NumberFormat", rAxisProps, mrExport ); + aPropertyStates = mxExpPropMapper->Filter(mrExport, rAxisProps); + + if (!maSrcShellID.isEmpty() && !maDestShellID.isEmpty() && maSrcShellID != maDestShellID) + { + // Disable link to source number format property when pasting to + // a different doc shell. These shell ID's should be both empty + // during real ODF export. + disableLinkedNumberFormat(aPropertyStates, mxExpPropMapper->getPropertySetMapper()); + } + } + + bool bExportDateScale = false; + if( bExportContent ) + { + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, eDimension ); + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, eAxisName ); + AddAutoStyleAttribute( aPropertyStates ); // write style name + if( !rCategoriesRange.isEmpty() ) + bExportDateScale = lcl_exportAxisType( rChart2Axis, mrExport ); + + // open axis element + pAxis.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, true, true )); + } + else + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + aPropertyStates.clear(); + + //date scale + if( bExportDateScale ) + exportDateScale( rAxisProps ); + + Reference< beans::XPropertySet > xTitleProps; + Reference< beans::XPropertySet > xMajorGridProps; + Reference< beans::XPropertySet > xMinorGridProps; + Reference< chart::XAxis > xAxis( rAxisProps, uno::UNO_QUERY ); + if( xAxis.is() ) + { + xTitleProps = bHasTitle ? xAxis->getAxisTitle() : nullptr; + xMajorGridProps = bHasMajorGrid ? xAxis->getMajorGrid() : nullptr; + xMinorGridProps = bHasMinorGrid ? xAxis->getMinorGrid() : nullptr; + } + + // axis-title + exportAxisTitle( xTitleProps , bExportContent ); + + // categories if we have a categories chart + if( bExportContent && !rCategoriesRange.isEmpty() ) + { + mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, rCategoriesRange ); + SvXMLElementExport aCategories( mrExport, XML_NAMESPACE_CHART, XML_CATEGORIES, true, true ); + } + + // grid + exportGrid( xMajorGridProps, true, bExportContent ); + exportGrid( xMinorGridProps, false, bExportContent ); +} + +void SchXMLExportHelper_Impl::exportAxes( + const Reference< chart::XDiagram > & xDiagram, + const Reference< chart2::XDiagram > & xNewDiagram, + bool bExportContent ) +{ + SAL_WARN_IF( !xDiagram.is(), "xmloff.chart", "Invalid XDiagram as parameter" ); + if( ! xDiagram.is()) + return; + + // get some properties from document first + bool bHasXAxis = false, + bHasYAxis = false, + bHasZAxis = false, + bHasSecondaryXAxis = false, + bHasSecondaryYAxis = false; + bool bHasXAxisTitle = false, + bHasYAxisTitle = false, + bHasZAxisTitle = false, + bHasSecondaryXAxisTitle = false, + bHasSecondaryYAxisTitle = false; + bool bHasXAxisMajorGrid = false, + bHasXAxisMinorGrid = false, + bHasYAxisMajorGrid = false, + bHasYAxisMinorGrid = false, + bHasZAxisMajorGrid = false, + bHasZAxisMinorGrid = false; + + // get multiple properties using XMultiPropertySet + MultiPropertySetHandler aDiagramProperties (xDiagram); + + aDiagramProperties.Add ("HasXAxis", bHasXAxis); + aDiagramProperties.Add ("HasYAxis", bHasYAxis); + aDiagramProperties.Add ("HasZAxis", bHasZAxis); + aDiagramProperties.Add ("HasSecondaryXAxis", bHasSecondaryXAxis); + aDiagramProperties.Add ("HasSecondaryYAxis", bHasSecondaryYAxis); + + aDiagramProperties.Add ("HasXAxisTitle", bHasXAxisTitle); + aDiagramProperties.Add ("HasYAxisTitle", bHasYAxisTitle); + aDiagramProperties.Add ("HasZAxisTitle", bHasZAxisTitle); + aDiagramProperties.Add ("HasSecondaryXAxisTitle", bHasSecondaryXAxisTitle); + aDiagramProperties.Add ("HasSecondaryYAxisTitle", bHasSecondaryYAxisTitle); + + aDiagramProperties.Add ("HasXAxisGrid", bHasXAxisMajorGrid); + aDiagramProperties.Add ("HasYAxisGrid", bHasYAxisMajorGrid); + aDiagramProperties.Add ("HasZAxisGrid", bHasZAxisMajorGrid); + + aDiagramProperties.Add ("HasXAxisHelpGrid", bHasXAxisMinorGrid); + aDiagramProperties.Add ("HasYAxisHelpGrid", bHasYAxisMinorGrid); + aDiagramProperties.Add ("HasZAxisHelpGrid", bHasZAxisMinorGrid); + + if ( ! aDiagramProperties.GetProperties ()) + { + SAL_INFO("xmloff.chart", "Required properties not found in Chart diagram"); + } + + Reference< chart2::XCoordinateSystem > xCooSys( lcl_getCooSys(xNewDiagram) ); + + // write an axis element also if the axis itself is not visible, but a grid or a title + + OUString aCategoriesRange; + Reference< chart::XAxisSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY ); + OUString sChartType = xDiagram->getDiagramType(); + + // x axis + + Reference< css::chart2::XAxis > xNewAxis = lcl_getAxis( xCooSys, XML_X ); + if( xNewAxis.is() ) + { + Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(0) : nullptr, uno::UNO_QUERY ); + if( mbHasCategoryLabels && bExportContent ) + { + Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) ); + if( xCategories.is() ) + { + Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() ); + if( xValues.is() ) + { + Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY ); + maCategoriesRange = xValues->getSourceRangeRepresentation(); + aCategoriesRange = lcl_ConvertRange( maCategoriesRange, xNewDoc ); + } + } + } + exportAxis( XML_X, XML_PRIMARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasXAxisTitle, bHasXAxisMajorGrid, bHasXAxisMinorGrid, bExportContent, sChartType ); + aCategoriesRange.clear(); + } + + // secondary x axis + + xNewAxis = lcl_getAxis( xCooSys, XML_X, false ); + if( xNewAxis.is() ) + { + Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(0) : nullptr, uno::UNO_QUERY ); + exportAxis( XML_X, XML_SECONDARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryXAxisTitle, false, false, bExportContent, sChartType ); + } + + // y axis + + xNewAxis = lcl_getAxis( xCooSys, XML_Y ); + if( xNewAxis.is() ) + { + Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(1) : nullptr, uno::UNO_QUERY ); + exportAxis( XML_Y, XML_PRIMARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasYAxisTitle, bHasYAxisMajorGrid, bHasYAxisMinorGrid, bExportContent, sChartType ); + } + + // secondary y axis + + xNewAxis = lcl_getAxis( xCooSys, XML_Y, false ); + if( xNewAxis.is() ) + { + Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(1) : nullptr, uno::UNO_QUERY ); + exportAxis( XML_Y, XML_SECONDARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryYAxisTitle, false, false, bExportContent, sChartType ); + } + + // z axis + + xNewAxis = lcl_getAxis( xCooSys, XML_Z ); + if( xNewAxis.is() ) + { + Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(2) : nullptr, uno::UNO_QUERY ); + exportAxis( XML_Z, XML_PRIMARY_Z, xAxisProps, xNewAxis, aCategoriesRange, bHasZAxisTitle, bHasZAxisMajorGrid, bHasZAxisMinorGrid, bExportContent, sChartType ); + } +} + +namespace +{ + bool lcl_hasNoValuesButText( const uno::Reference< chart2::data::XDataSequence >& xDataSequence ) + { + if( !xDataSequence.is() ) + return false;//have no data + + Sequence< uno::Any > aData; + Reference< chart2::data::XNumericalDataSequence > xNumericalDataSequence( xDataSequence, uno::UNO_QUERY ); + if( xNumericalDataSequence.is() ) + { + const Sequence< double > aDoubles( xNumericalDataSequence->getNumericalData() ); + if (std::any_of(aDoubles.begin(), aDoubles.end(), [](double fDouble) { return !std::isnan( fDouble ); })) + return false;//have double value + } + else + { + aData = xDataSequence->getData(); + double fDouble = 0.0; + bool bHaveDouble = std::any_of(std::cbegin(aData), std::cend(aData), + [&fDouble](const uno::Any& rData) { return (rData >>= fDouble) && !std::isnan( fDouble ); }); + if (bHaveDouble) + return false;//have double value + } + //no values found + + Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY ); + if( xTextualDataSequence.is() ) + { + const uno::Sequence< OUString > aStrings( xTextualDataSequence->getTextualData() ); + if (std::any_of(aStrings.begin(), aStrings.end(), [](const OUString& rString) { return !rString.isEmpty(); })) + return true;//have text + } + else + { + if( !aData.hasElements() ) + aData = xDataSequence->getData(); + OUString aString; + bool bHaveText = std::any_of(std::cbegin(aData), std::cend(aData), + [&aString](const uno::Any& rData) { return (rData >>= aString) && !aString.isEmpty(); }); + if (bHaveText) + return true;//have text + } + //no doubles and no texts + return false; + } + +// ODF has the line and fill properties in a <style:style> element, which is referenced by the +// <chart:data-label> element. But LibreOffice has them as special label properties of the series +// or point respectively. The following method generates ODF from internal API name. +void lcl_createDataLabelProperties( + std::vector<XMLPropertyState>& rDataLabelPropertyStates, + const Reference<beans::XPropertySet>& xPropSet, + const rtl::Reference<XMLChartExportPropertyMapper>& xExpPropMapper) +{ + if (!xExpPropMapper.is() || !xPropSet.is()) + return; + + const uno::Reference<beans::XPropertySetInfo> xInfo(xPropSet->getPropertySetInfo()); + const uno::Reference<beans::XPropertyState> xPropState(xPropSet, uno::UNO_QUERY); + const rtl::Reference<XMLPropertySetMapper>& rPropertySetMapper( + xExpPropMapper->getPropertySetMapper()); + if (!xInfo.is() || !xPropState.is() || !rPropertySetMapper.is()) + return; + + struct API2ODFMapItem + { + OUString sAPIName; + sal_uInt16 nNameSpace; // from include/xmloff/xmlnamespace.hxx + OUString sLocalName; + API2ODFMapItem(const OUString& sAPI, const sal_uInt16 nNS, const OUString& sLocal) + : sAPIName(sAPI) + , nNameSpace(nNS) + , sLocalName(sLocal) + { + } + }; + + const API2ODFMapItem aLabelFoo2ODFArray[] + = { API2ODFMapItem("LabelBorderStyle", XML_NAMESPACE_DRAW, "stroke"), + API2ODFMapItem("LabelBorderWidth", XML_NAMESPACE_SVG, "stroke-width"), + API2ODFMapItem("LabelBorderColor", XML_NAMESPACE_SVG, "stroke-color"), + API2ODFMapItem("LabelBorderDashName", XML_NAMESPACE_DRAW, "stroke-dash"), + API2ODFMapItem("LabelBorderTransparency", XML_NAMESPACE_SVG, "stroke-opacity"), + API2ODFMapItem("LabelFillStyle", XML_NAMESPACE_DRAW, "fill"), + API2ODFMapItem("LabelFillBackground", XML_NAMESPACE_DRAW, "fill-hatch-solid"), + API2ODFMapItem("LabelFillHatchName", XML_NAMESPACE_DRAW, "fill-hatch-name"), + API2ODFMapItem("LabelFillColor", XML_NAMESPACE_DRAW, "fill-color") }; + + for (const auto& rIt : aLabelFoo2ODFArray) + { + if (!xInfo->hasPropertyByName(rIt.sAPIName) + || xPropState->getPropertyState(rIt.sAPIName) != beans::PropertyState_DIRECT_VALUE) + continue; + sal_Int32 nTargetIndex + = rPropertySetMapper->GetEntryIndex(rIt.nNameSpace, rIt.sLocalName, 0); + if (nTargetIndex < 0) + continue; + XMLPropertyState aDataLabelStateItem(nTargetIndex, + xPropSet->getPropertyValue(rIt.sAPIName)); + rDataLabelPropertyStates.emplace_back(aDataLabelStateItem); + } +} +} // anonymous namespace + +void SchXMLExportHelper_Impl::exportSeries( + const Reference< chart2::XDiagram > & xNewDiagram, + const awt::Size & rPageSize, + bool bExportContent, + bool bHasTwoYAxes ) +{ + Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( xNewDiagram, uno::UNO_QUERY ); + if( ! xBCooSysCnt.is()) + return; + Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY ); + + OUString aFirstXDomainRange; + OUString aFirstYDomainRange; + + std::vector< XMLPropertyState > aPropertyStates; + std::vector< XMLPropertyState > aDataLabelPropertyStates; + + const Sequence< Reference< chart2::XCoordinateSystem > > + aCooSysSeq( xBCooSysCnt->getCoordinateSystems()); + for( const auto& rCooSys : aCooSysSeq ) + { + Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY ); + if( ! xCTCnt.is()) + continue; + const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes()); + for( const auto& rChartType : aCTSeq ) + { + Reference< chart2::XDataSeriesContainer > xDSCnt( rChartType, uno::UNO_QUERY ); + if( ! xDSCnt.is()) + continue; + // note: if xDSCnt.is() then also aCTSeq[nCTIdx] + OUString aChartType( rChartType->getChartType()); + OUString aLabelRole = rChartType->getRoleOfSequenceForSeriesLabel(); + + // special export for stock charts + if ( aChartType == "com.sun.star.chart2.CandleStickChartType" ) + { + bool bJapaneseCandleSticks = false; + Reference< beans::XPropertySet > xCTProp( rChartType, uno::UNO_QUERY ); + if( xCTProp.is()) + xCTProp->getPropertyValue("Japanese") >>= bJapaneseCandleSticks; + exportCandleStickSeries( + xDSCnt->getDataSeries(), xNewDiagram, bJapaneseCandleSticks, bExportContent ); + continue; + } + + // export dataseries for current chart-type + Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries()); + for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeriesSeq.getLength(); ++nSeriesIdx ) + { + // export series + Reference< chart2::data::XDataSource > xSource( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY ); + if( xSource.is()) + { + std::unique_ptr<SvXMLElementExport> pSeries; + Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt( + xSource->getDataSequences()); + sal_Int32 nMainSequenceIndex = -1; + sal_Int32 nSeriesLength = 0; + bool bHasMeanValueLine = false; + Reference< beans::XPropertySet > xPropSet; + tLabelValuesDataPair aSeriesLabelValuesPair; + + // search for main sequence and create a series element + { + Reference< chart2::data::XDataSequence > xValuesSeq; + Reference< chart2::data::XDataSequence > xLabelSeq; + sal_Int32 nSeqIdx=0; + for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx ) + { + Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() ); + if( nMainSequenceIndex==-1 ) + { + OUString aRole; + Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY ); + if( xSeqProp.is()) + xSeqProp->getPropertyValue("Role") >>= aRole; + // "main" sequence + if( aRole == aLabelRole ) + { + xValuesSeq.set( xTempValueSeq ); + xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel()); + nMainSequenceIndex = nSeqIdx; + } + } + sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0)); + if( nSeriesLength < nSequenceLength ) + nSeriesLength = nSequenceLength; + } + + // have found the main sequence, then xValuesSeq and + // xLabelSeq contain those. Otherwise both are empty + { + sal_Int32 nAttachedAxis = chart::ChartAxisAssign::PRIMARY_Y; + // get property states for autostyles + try + { + xPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet( + aSeriesSeq[nSeriesIdx], mrExport.GetModel() ); + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Series not found or no XPropertySet" ); + continue; + } + if( xPropSet.is()) + { + // determine attached axis + try + { + Any aAny( xPropSet->getPropertyValue( "Axis" )); + aAny >>= nAttachedAxis; + + aAny = xPropSet->getPropertyValue( "MeanValue" ); + aAny >>= bHasMeanValueLine; + } + catch( const beans::UnknownPropertyException & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Required property not found in DataRowProperties" ); + } + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012) + { + lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport ); + lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport ); + } + + if( mxExpPropMapper.is()) + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + } + + if( bExportContent ) + { + if( bHasTwoYAxes ) + { + if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y ); + else + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y ); + } + + // write style name + AddAutoStyleAttribute( aPropertyStates ); + + if( xValuesSeq.is()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, + lcl_ConvertRange( + xValuesSeq->getSourceRangeRepresentation(), + xNewDoc )); + else + // #i75297# allow empty series, export empty range to have all ranges on import + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, OUString()); + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older + { + if (xPropSet.is()) + { + Any aAny = xPropSet->getPropertyValue("ShowLegendEntry"); + if (!aAny.get<bool>()) + { + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true)); + } + } + } + + if (xLabelSeq.is()) + { + // Check if the label is direct string value rather than a reference. + bool bHasString = false; + uno::Reference<beans::XPropertySet> xLSProp(xLabelSeq, uno::UNO_QUERY); + if (xLSProp.is()) + { + try + { + xLSProp->getPropertyValue("HasStringLabel") >>= bHasString; + } + catch (const beans::UnknownPropertyException&) {} + } + + OUString aRange = xLabelSeq->getSourceRangeRepresentation(); + + if (bHasString) + { + mrExport.AddAttribute( + XML_NAMESPACE_LO_EXT, XML_LABEL_STRING, aRange); + } + else + { + mrExport.AddAttribute( + XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, + lcl_ConvertRange( + xLabelSeq->getSourceRangeRepresentation(), xNewDoc)); + } + } + + if( xLabelSeq.is() || xValuesSeq.is() ) + aSeriesLabelValuesPair = tLabelValuesDataPair( xLabelSeq, xValuesSeq ); + + // chart-type for mixed types + enum XMLTokenEnum eCTToken( + SchXMLTools::getTokenByChartType( aChartType, false /* bUseOldNames */ )); + //@todo: get token for current charttype + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, + mrExport.GetNamespaceMap().GetQNameByKey( + XML_NAMESPACE_CHART, GetXMLToken( eCTToken ))); + + // open series element until end of for loop + pSeries.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true )); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + // remove property states for autostyles + aPropertyStates.clear(); + } + } + + // export domain elements if we have a series parent element + if( pSeries ) + { + // domain elements + if( bExportContent ) + { + bool bIsScatterChart = aChartType == "com.sun.star.chart2.ScatterChartType"; + bool bIsBubbleChart = aChartType == "com.sun.star.chart2.BubbleChartType"; + Reference< chart2::data::XDataSequence > xYValuesForBubbleChart; + if( bIsBubbleChart ) + { + Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-y" ) ); + if( xSequence.is() ) + { + xYValuesForBubbleChart = xSequence->getValues(); + if( !lcl_exportDomainForThisSequence( xYValuesForBubbleChart, aFirstYDomainRange, mrExport ) ) + xYValuesForBubbleChart = nullptr; + } + } + if( bIsScatterChart || bIsBubbleChart ) + { + Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-x" ) ); + if( xSequence.is() ) + { + Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() ); + if( lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport ) ) + m_aDataSequencesToExport.emplace_back( + uno::Reference< chart2::data::XDataSequence >(), xValues ); + } + else if( nSeriesIdx==0 ) + { + //might be that the categories are used as x-values (e.g. for date axis) -> export them accordingly + Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) ); + if( xCategories.is() ) + { + Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() ); + if( !lcl_hasNoValuesButText( xValues ) ) + lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport ); + } + } + } + if( xYValuesForBubbleChart.is() ) + m_aDataSequencesToExport.emplace_back( + uno::Reference< chart2::data::XDataSequence >(), xYValuesForBubbleChart ); + } + } + + // add sequences for main sequence after domain sequences, + // so that the export of the local table has the correct order + if( bExportContent && + (aSeriesLabelValuesPair.first.is() || aSeriesLabelValuesPair.second.is())) + m_aDataSequencesToExport.push_back( aSeriesLabelValuesPair ); + + // statistical objects: + // regression curves and mean value lines + if( bHasMeanValueLine && + xPropSet.is() && + mxExpPropMapper.is() ) + { + Reference< beans::XPropertySet > xStatProp; + try + { + Any aPropAny( xPropSet->getPropertyValue( "DataMeanValueProperties" )); + aPropAny >>= xStatProp; + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of series - optional DataMeanValueProperties not available" ); + } + + if( xStatProp.is() ) + { + aPropertyStates = mxExpPropMapper->Filter(mrExport, xStatProp); + + if( !aPropertyStates.empty() ) + { + // write element + if( bExportContent ) + { + // add style name attribute + AddAutoStyleAttribute( aPropertyStates ); + + SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_MEAN_VALUE, true, true ); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + } + } + } + + if( xPropSet.is() && + mxExpPropMapper.is() ) + { + exportRegressionCurve( aSeriesSeq[nSeriesIdx], rPageSize, bExportContent ); + } + + exportErrorBar( xPropSet,false, bExportContent ); // X ErrorBar + exportErrorBar( xPropSet,true, bExportContent ); // Y ErrorBar + + exportDataPoints( + uno::Reference< beans::XPropertySet >( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY ), + nSeriesLength, xNewDiagram, bExportContent ); + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + + // create <chart:data-label> child element if needed. + if (xPropSet.is() && mxExpPropMapper.is()) + { + // Generate style for <chart:data-label> child element + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012) + { + lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet, + mxExpPropMapper); + } + } + if (bExportContent) + { + if (!aDataLabelPropertyStates.empty()) + { + // write style name + AddAutoStyleAttribute(aDataLabelPropertyStates); + // Further content does currently not exist for a <chart:data-label> + // element as child of a <chart:series>. + SvXMLElementExport(mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true, + true); + } + } + else + { + // add the style for the to be <chart:data-label> too + if (!aDataLabelPropertyStates.empty()) + CollectAutoStyle(std::move(aDataLabelPropertyStates)); + } + aDataLabelPropertyStates.clear(); + + if (bExportContent && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older + { + Sequence< OUString > aSupportedMappings = rChartType->getSupportedPropertyRoles(); + exportPropertyMapping( xSource, aSupportedMappings ); + } + + // close series element + pSeries.reset(); + } + } + aPropertyStates.clear(); + aDataLabelPropertyStates.clear(); + } + } +} + +void SchXMLExportHelper_Impl::exportPropertyMapping( + const Reference< chart2::data::XDataSource > & xSource, const Sequence< OUString >& rSupportedMappings ) +{ + Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY ); + Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt( + xSource->getDataSequences()); + + for(const auto& rSupportedMapping : rSupportedMappings) + { + Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, rSupportedMapping ) ); + if(xSequence.is()) + { + Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() ); + if( xValues.is()) + { + mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_PROPERTY, rSupportedMapping); + mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_CELL_RANGE_ADDRESS, + lcl_ConvertRange( + xValues->getSourceRangeRepresentation(), + xNewDoc )); + SvXMLElementExport( mrExport, XML_NAMESPACE_LO_EXT, XML_PROPERTY_MAPPING, true, true ); + + // register range for data table export + m_aDataSequencesToExport.emplace_back( + uno::Reference< chart2::data::XDataSequence >(), xValues ); + } + } + } +} + +void SchXMLExportHelper_Impl::exportRegressionCurve( + const Reference< chart2::XDataSeries >& xSeries, + const awt::Size& rPageSize, + bool bExportContent ) +{ + OSL_ASSERT( mxExpPropMapper.is()); + + Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, uno::UNO_QUERY ); + if( !xRegressionCurveContainer.is() ) + return; + + const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves(); + + for( const auto& xRegCurve : aRegCurveSeq ) + { + std::vector< XMLPropertyState > aEquationPropertyStates; + if (!xRegCurve.is()) + continue; + + Reference< beans::XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY ); + if( !xProperties.is() ) + continue; + + Reference< lang::XServiceName > xServiceName( xProperties, uno::UNO_QUERY ); + if( !xServiceName.is() ) + continue; + + bool bShowEquation = false; + bool bShowRSquared = false; + bool bExportEquation = false; + + OUString aService = xServiceName->getServiceName(); + + std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xProperties); + + // Add service name (which is regression type) + sal_Int32 nIndex = GetPropertySetMapper()->FindEntryIndex(XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE); + XMLPropertyState property(nIndex, uno::Any(aService)); + aPropertyStates.push_back(property); + + Reference< beans::XPropertySet > xEquationProperties; + xEquationProperties.set( xRegCurve->getEquationProperties() ); + if( xEquationProperties.is()) + { + xEquationProperties->getPropertyValue( "ShowEquation") >>= bShowEquation; + xEquationProperties->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowRSquared; + + bExportEquation = ( bShowEquation || bShowRSquared ); + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentVersion < SvtSaveOptions::ODFSVER_012) + { + bExportEquation=false; + } + if( bExportEquation ) + { + // number format + sal_Int32 nNumberFormat = 0; + if( (xEquationProperties->getPropertyValue("NumberFormat") >>= nNumberFormat ) && + nNumberFormat != -1 ) + { + mrExport.addDataStyle( nNumberFormat ); + } + aEquationPropertyStates = mxExpPropMapper->Filter(mrExport, xEquationProperties); + } + } + + if( !aPropertyStates.empty() || bExportEquation ) + { + // write element + if( bExportContent ) + { + // add style name attribute + if( !aPropertyStates.empty()) + { + AddAutoStyleAttribute( aPropertyStates ); + } + + SvXMLElementExport aRegressionExport( mrExport, XML_NAMESPACE_CHART, XML_REGRESSION_CURVE, true, true ); + if( bExportEquation ) + { + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_EQUATION, (bShowEquation ? XML_TRUE : XML_FALSE) ); + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_R_SQUARE, (bShowRSquared ? XML_TRUE : XML_FALSE) ); + + // export position + chart2::RelativePosition aRelativePosition; + if( xEquationProperties->getPropertyValue( "RelativePosition" ) >>= aRelativePosition ) + { + double fX = aRelativePosition.Primary * rPageSize.Width; + double fY = aRelativePosition.Secondary * rPageSize.Height; + awt::Point aPos; + aPos.X = static_cast< sal_Int32 >( ::rtl::math::round( fX )); + aPos.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY )); + addPosition( aPos ); + } + + if( !aEquationPropertyStates.empty()) + { + AddAutoStyleAttribute( aEquationPropertyStates ); + } + + SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_EQUATION, true, true ); + } + } + else // autostyles + { + if( !aPropertyStates.empty()) + { + CollectAutoStyle( std::move(aPropertyStates) ); + } + if( bExportEquation && !aEquationPropertyStates.empty()) + { + CollectAutoStyle( std::move(aEquationPropertyStates) ); + } + } + } + } +} + +void SchXMLExportHelper_Impl::exportErrorBar( const Reference<beans::XPropertySet> &xSeriesProp, + bool bYError, bool bExportContent ) +{ + assert(mxExpPropMapper.is()); + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion( + mrExport.getSaneDefaultVersion()); + + /// Don't export X ErrorBars for older ODF versions. + if (!bYError && nCurrentVersion < SvtSaveOptions::ODFSVER_012) + return; + + if (!xSeriesProp.is()) + return; + + bool bNegative = false, bPositive = false; + sal_Int32 nErrorBarStyle = chart::ErrorBarStyle::NONE; + Reference< beans::XPropertySet > xErrorBarProp; + + try + { + Any aAny = xSeriesProp->getPropertyValue( bYError ? OUString("ErrorBarY") : OUString("ErrorBarX") ); + aAny >>= xErrorBarProp; + + if ( xErrorBarProp.is() ) + { + aAny = xErrorBarProp->getPropertyValue("ShowNegativeError" ); + aAny >>= bNegative; + + aAny = xErrorBarProp->getPropertyValue("ShowPositiveError" ); + aAny >>= bPositive; + + aAny = xErrorBarProp->getPropertyValue("ErrorBarStyle" ); + aAny >>= nErrorBarStyle; + } + } + catch( const beans::UnknownPropertyException & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Required property not found in DataRowProperties" ); + } + + if( !(nErrorBarStyle != chart::ErrorBarStyle::NONE && (bNegative || bPositive))) + return; + + if( bExportContent && nErrorBarStyle == chart::ErrorBarStyle::FROM_DATA ) + { + // register data ranges for error bars for export in local table + ::std::vector< Reference< chart2::data::XDataSequence > > aErrorBarSequences( + lcl_getErrorBarSequences( xErrorBarProp )); + for( const auto& rErrorBarSequence : aErrorBarSequences ) + { + m_aDataSequencesToExport.emplace_back( + uno::Reference< chart2::data::XDataSequence >(), rErrorBarSequence ); + } + } + + std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xErrorBarProp); + + if( aPropertyStates.empty() ) + return; + + // write element + if( bExportContent ) + { + // add style name attribute + AddAutoStyleAttribute( aPropertyStates ); + + if (nCurrentVersion >= SvtSaveOptions::ODFSVER_012) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, bYError ? XML_Y : XML_X );//#i114149# + SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_ERROR_INDICATOR, true, true ); + } + else // autostyles + { + CollectAutoStyle( std::move(aPropertyStates) ); + } +} + +void SchXMLExportHelper_Impl::exportCandleStickSeries( + const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq, + const Reference< chart2::XDiagram > & xDiagram, + bool bJapaneseCandleSticks, + bool bExportContent ) +{ + + for( const auto& xSeries : aSeriesSeq ) + { + sal_Int32 nAttachedAxis = lcl_isSeriesAttachedToFirstAxis( xSeries ) + ? chart::ChartAxisAssign::PRIMARY_Y + : chart::ChartAxisAssign::SECONDARY_Y; + + Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY ); + if( xSource.is()) + { + // export series in correct order (as we don't store roles) + // with japanese candlesticks: open, low, high, close + // otherwise: low, high, close + Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt( + xSource->getDataSequences()); + + sal_Int32 nSeriesLength = + lcl_getSequenceLengthByRole( aSeqCnt, "values-last"); + + if( bExportContent ) + { + Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY ); + //@todo: export data points + + //TODO: moggi: same code three times + // open + if( bJapaneseCandleSticks ) + { + tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole( + aSeqCnt, "values-first", xNewDoc, m_aDataSequencesToExport )); + if( !aRanges.second.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second ); + if( !aRanges.first.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first ); + if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y ); + else + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y ); + SvXMLElementExport aOpenSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true ); + // export empty data points + exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent ); + } + + // low + { + tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole( + aSeqCnt, "values-min", xNewDoc, m_aDataSequencesToExport )); + if( !aRanges.second.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second ); + if( !aRanges.first.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first ); + if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y ); + else + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y ); + SvXMLElementExport aLowSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true ); + // export empty data points + exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent ); + } + + // high + { + tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole( + aSeqCnt, "values-max", xNewDoc, m_aDataSequencesToExport )); + if( !aRanges.second.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second ); + if( !aRanges.first.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first ); + if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y ); + else + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y ); + SvXMLElementExport aHighSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true ); + // export empty data points + exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent ); + } + + // close + { + tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole( + aSeqCnt, "values-last", xNewDoc, m_aDataSequencesToExport )); + if( !aRanges.second.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second ); + if( !aRanges.first.isEmpty()) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first ); + if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y ); + else + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y ); + SvXMLElementExport aCloseSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true ); + // export empty data points + exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent ); + } + } + else // autostyles + { + // for close series + } + // remove property states for autostyles + } + } +} + +void SchXMLExportHelper_Impl::exportDataPoints( + const uno::Reference< beans::XPropertySet > & xSeriesProperties, + sal_Int32 nSeriesLength, + const uno::Reference< chart2::XDiagram > & xDiagram, + bool bExportContent ) +{ + // data-points + + // write data-points only if they contain autostyles + // objects with equal autostyles are grouped using the attribute + // repeat="number" + + // Note: if only the nth data-point has autostyles there is an element + // without style and repeat="n-1" attribute written in advance. + + // the sequence aDataPointSeq contains indices of data-points that + // do have own attributes. This increases the performance substantially. + + // more performant version for #93600# + if (!mxExpPropMapper.is()) + return; + + uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY ); + + std::vector< XMLPropertyState > aPropertyStates; + std::vector<XMLPropertyState> aDataLabelPropertyStates; + + bool bVaryColorsByPoint = false; + Sequence< sal_Int32 > aDataPointSeq; + Sequence<sal_Int32> deletedLegendEntriesSeq; + if( xSeriesProperties.is()) + { + xSeriesProperties->getPropertyValue("AttributedDataPoints") >>= aDataPointSeq; + xSeriesProperties->getPropertyValue("VaryColorsByPoint") >>= bVaryColorsByPoint; + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older + xSeriesProperties->getPropertyValue("DeletedLegendEntries") >>= deletedLegendEntriesSeq; + } + + sal_Int32 nSize = aDataPointSeq.getLength(); + SAL_WARN_IF( nSize > nSeriesLength, "xmloff.chart", "Too many point attributes" ); + + const sal_Int32 * pPoints = aDataPointSeq.getConstArray(); + sal_Int32 nElement; + Reference< chart2::XColorScheme > xColorScheme; + if( xDiagram.is()) + xColorScheme.set( xDiagram->getDefaultColorScheme()); + + ::std::vector< SchXMLDataPointStruct > aDataPointVector; + + sal_Int32 nLastIndex = -1; + + // collect elements + if( bVaryColorsByPoint && xColorScheme.is() ) + { + o3tl::sorted_vector< sal_Int32 > aAttrPointSet; + aAttrPointSet.reserve(aDataPointSeq.getLength()); + for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p) + aAttrPointSet.insert( *p ); + const auto aEndIt = aAttrPointSet.end(); + for( nElement = 0; nElement < nSeriesLength; ++nElement ) + { + aPropertyStates.clear(); + aDataLabelPropertyStates.clear(); + uno::Reference< beans::XPropertySet > xPropSet; + bool bExportNumFmt = false; + if( aAttrPointSet.find( nElement ) != aEndIt ) + { + try + { + xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet( + xSeries, nElement, mrExport.GetModel() ); + bExportNumFmt = true; + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of data point" ); + } + } + else + { + // property set only containing the color + xPropSet.set( new ::xmloff::chart::ColorPropertySet( + ::Color(ColorTransparency, xColorScheme->getColorByIndex( nElement )))); + } + SAL_WARN_IF( !xPropSet.is(), "xmloff.chart", "Pie Segments should have properties" ); + if( xPropSet.is()) + { + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012 && bExportNumFmt) + { + lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport ); + lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport ); + } + + // Generate style for <chart:data-label> child element + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012) + { + lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet, + mxExpPropMapper); + } + + if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) + { + sal_Int32 nPlacement = 0; + xPropSet->getPropertyValue("LabelPlacement") >>= nPlacement; + if (nPlacement == chart::DataLabelPlacement::CUSTOM) + { + xPropSet->setPropertyValue("LabelPlacement", + uno::Any(chart::DataLabelPlacement::OUTSIDE)); + } + } + + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty()) + { + if (bExportContent) + { + // write data-point with style + SchXMLDataPointStruct aPoint; + if (!aPropertyStates.empty()) + { + SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart", + "Autostyle queue empty!"); + aPoint.maStyleName = maAutoStyleNameQueue.front(); + maAutoStyleNameQueue.pop(); + } + if (!aDataLabelPropertyStates.empty()) + { + SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart", + "Autostyle queue empty!"); + aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front(); + maAutoStyleNameQueue.pop(); + } + if(bExportNumFmt) + aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nElement, xSeries); + aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nElement, xSeries); + + aDataPointVector.push_back( aPoint ); + } + else + { + if (!aPropertyStates.empty()) + CollectAutoStyle(std::move(aPropertyStates)); + if (!aDataLabelPropertyStates.empty()) + CollectAutoStyle(std::move(aDataLabelPropertyStates)); + } + } + } + } + SAL_WARN_IF( bExportContent && (static_cast<sal_Int32>(aDataPointVector.size()) != nSeriesLength), "xmloff.chart", "not enough data points on content export" ); + } + else + { + for( sal_Int32 nCurrIndex : std::as_const(aDataPointSeq) ) + { + aPropertyStates.clear(); + aDataLabelPropertyStates.clear(); + //assuming sorted indices in pPoints + + if( nCurrIndex<0 || nCurrIndex>=nSeriesLength ) + break; + + // write leading empty data points + if( nCurrIndex - nLastIndex > 1 ) + { + SchXMLDataPointStruct aPoint; + aPoint.mnRepeat = nCurrIndex - nLastIndex - 1; + aDataPointVector.push_back( aPoint ); + } + + uno::Reference< beans::XPropertySet > xPropSet; + // get property states + try + { + xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet( + xSeries, nCurrIndex, mrExport.GetModel() ); + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of data point" ); + } + if( xPropSet.is()) + { + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + mrExport.getSaneDefaultVersion()); + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012) + { + lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport ); + lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport ); + } + + // Generate style for <chart:data-label> child element + if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012) + { + lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet, + mxExpPropMapper); + } + + aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet); + + if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty()) + { + if( bExportContent ) + { + // write data-point with style + SchXMLDataPointStruct aPoint; + if (!aPropertyStates.empty()) + { + SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart", + "Autostyle queue empty!"); + aPoint.maStyleName = maAutoStyleNameQueue.front(); + maAutoStyleNameQueue.pop(); + } + aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries); + aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nCurrIndex, xSeries); + if (!aDataLabelPropertyStates.empty()) + { + SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart", + "Autostyle queue empty!"); + aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front(); + maAutoStyleNameQueue.pop(); + } + + aDataPointVector.push_back( aPoint ); + nLastIndex = nCurrIndex; + } + else + { + if (!aPropertyStates.empty()) + CollectAutoStyle(std::move(aPropertyStates)); + if (!aDataLabelPropertyStates.empty()) + CollectAutoStyle(std::move(aDataLabelPropertyStates)); + } + continue; + } + } + + // if we get here the property states are empty + SchXMLDataPointStruct aPoint; + aDataPointVector.push_back( aPoint ); + + nLastIndex = nCurrIndex; + } + // final empty elements + sal_Int32 nRepeat = nSeriesLength - nLastIndex - 1; + if( nRepeat > 0 ) + { + SchXMLDataPointStruct aPoint; + aPoint.mnRepeat = nRepeat; + aDataPointVector.push_back( aPoint ); + } + } + + if (!bExportContent) + return; + + // write elements (merge equal ones) + SchXMLDataPointStruct aPoint; + SchXMLDataPointStruct aLastPoint; + + // initialize so that it doesn't matter if + // the element is counted in the first iteration + aLastPoint.mnRepeat = 0; + sal_Int32 nIndex = 0; + for( const auto& rPoint : aDataPointVector ) + { + aPoint = rPoint; + + if (aPoint.maStyleName == aLastPoint.maStyleName + && aLastPoint.mCustomLabel.maFields.getLength() < 1 + && aLastPoint.mCustomLabelPos.Primary == 0.0 + && aLastPoint.mCustomLabelPos.Secondary == 0.0 + && aPoint.msDataLabelStyleName == aLastPoint.msDataLabelStyleName) + aPoint.mnRepeat += aLastPoint.mnRepeat; + else if( aLastPoint.mnRepeat > 0 ) + { + // write last element + if( !aLastPoint.maStyleName.isEmpty() ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName ); + + if( aLastPoint.mnRepeat > 1 ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED, + OUString::number( aLastPoint.mnRepeat )); + + for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq)) + { + if (nIndex == deletedLegendEntry) + { + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true)); + break; + } + } + nIndex++; + exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes + SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true ); + exportCustomLabel(aLastPoint); + } + aLastPoint = aPoint; + } + // write last element if it hasn't been written in last iteration + if( aPoint.maStyleName != aLastPoint.maStyleName ) + return; + + if( !aLastPoint.maStyleName.isEmpty() ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName ); + + if( aLastPoint.mnRepeat > 1 ) + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED, + OUString::number( aLastPoint.mnRepeat )); + + for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq)) + { + if (nIndex == deletedLegendEntry) + { + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true)); + break; + } + } + + exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes + SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true ); + exportCustomLabel(aLastPoint); +} + +void SchXMLExportHelper_Impl::exportCustomLabel(const SchXMLDataPointStruct& rPoint) +{ + if (rPoint.mCustomLabel.maFields.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty()) + return; // nothing to export + + if (!rPoint.msDataLabelStyleName.isEmpty()) + mrExport.AddAttribute(XML_NAMESPACE_CHART, XML_STYLE_NAME, rPoint.msDataLabelStyleName); + + if (rPoint.mCustomLabel.mbDataLabelsRange) + { + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABELS_CELL_RANGE, rPoint.mCustomLabel.maRange); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABEL_GUID, rPoint.mCustomLabel.maGuid); + } + // TODO svg:x and svg:y for <chart:data-label> + SvXMLElementExport aLabelElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true, true); + SvXMLElementExport aPara( mrExport, XML_NAMESPACE_TEXT, XML_P, true, false ); + + for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabel.maFields) + { + // TODO add style + SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false); + mrExport.GetDocHandler()->characters(label->getString()); + } +} + +void SchXMLExportHelper_Impl::exportCustomLabelPosition( const chart2::RelativePosition & xCustomLabelPosition) +{ + if( xCustomLabelPosition.Primary == 0.0 && xCustomLabelPosition.Secondary == 0.0 ) + return; // nothing to export + + OUStringBuffer aCustomLabelPosString; + ::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Primary); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_X, aCustomLabelPosString.makeStringAndClear()); + + ::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Secondary); + mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_Y, aCustomLabelPosString.makeStringAndClear()); +} + +void SchXMLExportHelper_Impl::addPosition( const awt::Point & rPosition ) +{ + mrExport.GetMM100UnitConverter().convertMeasureToXML( + msStringBuffer, rPosition.X ); + msString = msStringBuffer.makeStringAndClear(); + mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X, msString ); + + mrExport.GetMM100UnitConverter().convertMeasureToXML( + msStringBuffer, rPosition.Y ); + msString = msStringBuffer.makeStringAndClear(); + mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y, msString ); +} + +void SchXMLExportHelper_Impl::addPosition( const Reference< drawing::XShape >& xShape ) +{ + if( xShape.is()) + addPosition( xShape->getPosition()); +} + +void SchXMLExportHelper_Impl::addSize( const awt::Size & rSize, bool bIsOOoNamespace) +{ + mrExport.GetMM100UnitConverter().convertMeasureToXML( + msStringBuffer, rSize.Width ); + msString = msStringBuffer.makeStringAndClear(); + mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG , XML_WIDTH, msString ); + + mrExport.GetMM100UnitConverter().convertMeasureToXML( + msStringBuffer, rSize.Height); + msString = msStringBuffer.makeStringAndClear(); + mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG, XML_HEIGHT, msString ); +} + +void SchXMLExportHelper_Impl::addSize( const Reference< drawing::XShape >& xShape ) +{ + if( xShape.is()) + addSize( xShape->getSize() ); +} + +awt::Size SchXMLExportHelper_Impl::getPageSize( const Reference< chart2::XChartDocument > & xChartDoc ) +{ + awt::Size aSize( 8000, 7000 ); + uno::Reference< embed::XVisualObject > xVisualObject( xChartDoc, uno::UNO_QUERY ); + SAL_WARN_IF( !xVisualObject.is(), "xmloff.chart", "need XVisualObject for page size" ); + if( xVisualObject.is() ) + aSize = xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + + return aSize; +} + +void SchXMLExportHelper_Impl::CollectAutoStyle( std::vector< XMLPropertyState >&& aStates ) +{ + if( !aStates.empty() ) + maAutoStyleNameQueue.push( mrAutoStylePool.Add( XmlStyleFamily::SCH_CHART_ID, std::move(aStates) )); +} + +void SchXMLExportHelper_Impl::AddAutoStyleAttribute( const std::vector< XMLPropertyState >& aStates ) +{ + if( !aStates.empty() ) + { + SAL_WARN_IF( maAutoStyleNameQueue.empty(), "xmloff.chart", "Autostyle queue empty!" ); + + mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, maAutoStyleNameQueue.front() ); + maAutoStyleNameQueue.pop(); + } +} + +void SchXMLExportHelper_Impl::exportText( const OUString& rText ) +{ + SchXMLTools::exportText( mrExport, rText, false/*bConvertTabsLFs*/ ); +} + + +SchXMLExport::SchXMLExport(const Reference<uno::XComponentContext>& xContext, + OUString const& implementationName, SvXMLExportFlags nExportFlags) + : SvXMLExport(xContext, implementationName, util::MeasureUnit::CM, ::xmloff::token::XML_CHART, + nExportFlags) + , maAutoStylePool(new SchXMLAutoStylePoolP(*this)) + , maExportHelper(new SchXMLExportHelper(*this, *maAutoStylePool)) +{ + if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + GetNamespaceMap_().Add( GetXMLToken(XML_NP_CHART_EXT), GetXMLToken(XML_N_CHART_EXT), XML_NAMESPACE_CHART_EXT); +} + +SchXMLExport::~SchXMLExport() +{ +} + +ErrCode SchXMLExport::exportDoc( enum ::xmloff::token::XMLTokenEnum eClass ) +{ + maExportHelper->SetSourceShellID(GetSourceShellID()); + maExportHelper->SetDestinationShellID(GetDestinationShellID()); + + Reference< chart2::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY ); + maExportHelper->m_pImpl->InitRangeSegmentationProperties( xChartDoc ); + return SvXMLExport::exportDoc( eClass ); +} + +void SchXMLExport::ExportMasterStyles_() +{ + // not available in chart + SAL_INFO("xmloff.chart", "Master Style Export requested. Not available for Chart" ); +} + +void SchXMLExport::collectAutoStyles() +{ + SvXMLExport::collectAutoStyles(); + + if (mbAutoStylesCollected) + return; + + // there are no styles that require their own autostyles + if( getExportFlags() & SvXMLExportFlags::CONTENT ) + { + Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY ); + if( xChartDoc.is()) + { + maExportHelper->m_pImpl->collectAutoStyles( xChartDoc ); + } + else + { + SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel (must be XChartDocument)" ); + } + } + mbAutoStylesCollected = true; +} + +void SchXMLExport::ExportAutoStyles_() +{ + collectAutoStyles(); + + if( getExportFlags() & SvXMLExportFlags::CONTENT ) + { + Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY ); + if( xChartDoc.is()) + { + maExportHelper->m_pImpl->exportAutoStyles(); + } + else + { + SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel (must be XChartDocument)" ); + } + } +} + +void SchXMLExport::ExportContent_() +{ + Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY ); + if( xChartDoc.is()) + { + // determine if data comes from the outside + bool bIncludeTable = true; + + Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY ); + if( xNewDoc.is()) + { + // check if we have own data. If so we must not export the complete + // range string, as this is our only indicator for having own or + // external data. @todo: fix this in the file format! + Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY ); + if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" )) + { + bIncludeTable = false; + } + } + else + { + Reference< lang::XServiceInfo > xServ( xChartDoc, uno::UNO_QUERY ); + if( xServ.is()) + { + if( xServ->supportsService( "com.sun.star.chart.ChartTableAddressSupplier" )) + { + Reference< beans::XPropertySet > xProp( xServ, uno::UNO_QUERY ); + if( xProp.is()) + { + Any aAny; + try + { + OUString sChartAddress; + aAny = xProp->getPropertyValue( "ChartRangeAddress" ); + aAny >>= sChartAddress; + maExportHelper->m_pImpl->SetChartRangeAddress( sChartAddress ); + + // do not include own table if there are external addresses + bIncludeTable = sChartAddress.isEmpty(); + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Property ChartRangeAddress not supported by ChartDocument" ); + } + } + } + } + } + maExportHelper->m_pImpl->exportChart( xChartDoc, bIncludeTable ); + } + else + { + SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel" ); + } +} + +rtl::Reference< XMLPropertySetMapper > const & SchXMLExport::GetPropertySetMapper() const +{ + return maExportHelper->m_pImpl->GetPropertySetMapper(); +} + +void SchXMLExportHelper_Impl::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc ) +{ + if( !xChartDoc.is()) + return; + + try + { + Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() ); + SAL_WARN_IF( !xDataProvider.is(), "xmloff.chart", "No DataProvider" ); + if( xDataProvider.is()) + { + Reference< chart2::data::XDataSource > xDataSource( lcl_pressUsedDataIntoRectangularFormat( xChartDoc, mbHasCategoryLabels )); + const Sequence< beans::PropertyValue > aArgs( xDataProvider->detectArguments( xDataSource )); + OUString sCellRange, sBrokenRange; + bool bBrokenRangeAvailable = false; + for( const auto& rArg : aArgs ) + { + if ( rArg.Name == "CellRangeRepresentation" ) + rArg.Value >>= sCellRange; + else if ( rArg.Name == "BrokenCellRangeForExport" ) + { + if( rArg.Value >>= sBrokenRange ) + bBrokenRangeAvailable = true; + } + else if ( rArg.Name == "DataRowSource" ) + { + chart::ChartDataRowSource eRowSource; + rArg.Value >>= eRowSource; + mbRowSourceColumns = ( eRowSource == chart::ChartDataRowSource_COLUMNS ); + } + else if ( rArg.Name == "SequenceMapping" ) + rArg.Value >>= maSequenceMapping; + } + + // #i79009# For Writer we have to export a broken version of the + // range, where every row number is not too large, so that older + // version can correctly read those files. + msChartAddress = (bBrokenRangeAvailable ? sBrokenRange : sCellRange); + if( !msChartAddress.isEmpty() ) + { + // convert format to XML-conform one + Reference< chart2::data::XRangeXMLConversion > xConversion( xDataProvider, uno::UNO_QUERY ); + if( xConversion.is()) + msChartAddress = xConversion->convertRangeToXML( msChartAddress ); + } + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } +} + +// first version: everything goes in one storage + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLExporter_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire( + new SchXMLExport(pCtx, "SchXMLExport.Compact", + SvXMLExportFlags::ALL + ^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES + | SvXMLExportFlags::SCRIPTS))); +} + +// Oasis format +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisExporter_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire( + new SchXMLExport(pCtx, "SchXMLExport.Oasis.Compact", + (SvXMLExportFlags::ALL + ^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES + | SvXMLExportFlags::SCRIPTS)) + | SvXMLExportFlags::OASIS)); +} + +// multiple storage version: one for content / styles / meta + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLStylesExporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Styles", SvXMLExportFlags::STYLES)); +} + +// Oasis format +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisStylesExporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Styles", + SvXMLExportFlags::STYLES | SvXMLExportFlags::OASIS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLContentExporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Content", + SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT + | SvXMLExportFlags::FONTDECLS)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisContentExporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Content", + SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT + | SvXMLExportFlags::FONTDECLS + | SvXMLExportFlags::OASIS)); +} + +// Oasis format + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisMetaExporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Meta", + SvXMLExportFlags::META | SvXMLExportFlags::OASIS)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLImport.cxx b/xmloff/source/chart/SchXMLImport.cxx new file mode 100644 index 000000000..a39f82245 --- /dev/null +++ b/xmloff/source/chart/SchXMLImport.cxx @@ -0,0 +1,377 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <SchXMLImport.hxx> +#include "SchXMLChartContext.hxx" +#include "contexts.hxx" +#include "SchXMLTools.hxx" + +#include <sal/log.hxx> +#include <comphelper/processfactory.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/prstylei.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/xmlstyle.hxx> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> + +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> + +#include <tools/diagnose_ex.h> + +using namespace com::sun::star; +using namespace ::xmloff::token; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ +class lcl_MatchesChartType +{ +public: + explicit lcl_MatchesChartType( const OUString & aChartTypeName ) : + m_aChartTypeName( aChartTypeName ) + {} + + bool operator () ( const Reference< chart2::XChartType > & xChartType ) const + { + return (xChartType.is() && + xChartType->getChartType() == m_aChartTypeName ); + } + +private: + OUString m_aChartTypeName; +}; +} // anonymous namespace + + // TokenMaps for distinguishing different + // tokens in different contexts + +// element maps + +// attribute maps + +SchXMLImportHelper::SchXMLImportHelper() : + mpAutoStyles( nullptr ) +{ +} + +SvXMLImportContext* SchXMLImportHelper::CreateChartContext( + SvXMLImport& rImport, + const Reference< frame::XModel >& rChartModel ) +{ + SvXMLImportContext* pContext = nullptr; + + Reference< chart::XChartDocument > xDoc( rChartModel, uno::UNO_QUERY ); + if( xDoc.is()) + { + mxChartDoc = xDoc; + pContext = new SchXMLChartContext( *this, rImport ); + } + else + { + SAL_WARN("xmloff.chart", "No valid XChartDocument given as XModel" ); + } + + return pContext; +} + +void SchXMLImportHelper::FillAutoStyle(const OUString& rAutoStyleName, const uno::Reference<beans::XPropertySet>& rProp) +{ + if (!rProp.is()) + return; + + const SvXMLStylesContext* pStylesCtxt = GetAutoStylesContext(); + if (pStylesCtxt) + { + SvXMLStyleContext* pStyle = const_cast<SvXMLStyleContext*>(pStylesCtxt->FindStyleChildContext(SchXMLImportHelper::GetChartFamilyID(), rAutoStyleName)); + + if (XMLPropStyleContext* pPropStyle = dynamic_cast<XMLPropStyleContext*>(pStyle)) + pPropStyle->FillPropertySet(rProp); + } +} + +// get various token maps + + +//static +void SchXMLImportHelper::DeleteDataSeries( + const Reference< chart2::XDataSeries > & xSeries, + const Reference< chart2::XChartDocument > & xDoc ) +{ + if( !xDoc.is() ) + return; + try + { + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( + xDoc->getFirstDiagram(), uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( + xCooSysCnt->getCoordinateSystems()); + + for( const auto& rCooSys : aCooSysSeq ) + { + Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes()); + + for( const auto& rChartType : aChartTypes ) + { + Reference< chart2::XDataSeriesContainer > xSeriesCnt( rChartType, uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xSeriesCnt->getDataSeries()); + + if (std::find(aSeriesSeq.begin(), aSeriesSeq.end(), xSeries) != aSeriesSeq.end()) + { + xSeriesCnt->removeDataSeries(xSeries); + return; + } + } + } + } + catch( const uno::Exception &) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } +} + +// static +Reference< chart2::XDataSeries > SchXMLImportHelper::GetNewDataSeries( + const Reference< chart2::XChartDocument > & xDoc, + sal_Int32 nCoordinateSystemIndex, + const OUString & rChartTypeName, + bool bPushLastChartType /* = false */ ) +{ + Reference< chart2::XDataSeries > xResult; + if(!xDoc.is()) + return xResult; + + try + { + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( + xDoc->getFirstDiagram(), uno::UNO_QUERY_THROW ); + Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( + xCooSysCnt->getCoordinateSystems()); + Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + + if( nCoordinateSystemIndex < aCooSysSeq.getLength()) + { + Reference< chart2::XChartType > xCurrentType; + { + Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[ nCoordinateSystemIndex ], uno::UNO_QUERY_THROW ); + Sequence< Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes()); + // find matching chart type group + const Reference< chart2::XChartType > * pBegin = aChartTypes.getConstArray(); + const Reference< chart2::XChartType > * pEnd = pBegin + aChartTypes.getLength(); + const Reference< chart2::XChartType > * pIt = + ::std::find_if( pBegin, pEnd, lcl_MatchesChartType( rChartTypeName )); + if( pIt != pEnd ) + xCurrentType.set( *pIt ); + // if chart type is set at series and differs from current one, + // create a new chart type + if( !xCurrentType.is()) + { + xCurrentType.set( + xContext->getServiceManager()->createInstanceWithContext( rChartTypeName, xContext ), + uno::UNO_QUERY ); + if( xCurrentType.is()) + { + if( bPushLastChartType && aChartTypes.hasElements()) + { + sal_Int32 nIndex( aChartTypes.getLength() - 1 ); + aChartTypes.realloc( aChartTypes.getLength() + 1 ); + auto pChartTypes = aChartTypes.getArray(); + pChartTypes[ nIndex + 1 ] = aChartTypes[ nIndex ]; + pChartTypes[ nIndex ] = xCurrentType; + xCTCnt->setChartTypes( aChartTypes ); + } + else + xCTCnt->addChartType( xCurrentType ); + } + } + } + + if( xCurrentType.is()) + { + Reference< chart2::XDataSeriesContainer > xSeriesCnt( xCurrentType, uno::UNO_QUERY_THROW ); + + if( xContext.is() ) + { + xResult.set( + xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.chart2.DataSeries", + xContext ), uno::UNO_QUERY_THROW ); + } + if( xResult.is() ) + xSeriesCnt->addDataSeries( xResult ); + } + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + return xResult; +} + +SchXMLImport::SchXMLImport( + const Reference< uno::XComponentContext >& xContext, + OUString const & implementationName, SvXMLImportFlags nImportFlags ) : + SvXMLImport( xContext, implementationName, nImportFlags ), + maImportHelper(new SchXMLImportHelper) +{ + GetNamespaceMap().Add( GetXMLToken(XML_NP_XLINK), GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK ); + GetNamespaceMap().Add( GetXMLToken(XML_NP_CHART_EXT), GetXMLToken(XML_N_CHART_EXT), XML_NAMESPACE_CHART_EXT); +} + +SchXMLImport::~SchXMLImport() noexcept +{ + uno::Reference< chart2::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY ); + if( xChartDoc.is() && xChartDoc->hasControllersLocked() ) + xChartDoc->unlockControllers(); +} + +// create the main context (subcontexts are created +// by the one created here) +SvXMLImportContext *SchXMLImport::CreateFastContext( sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + SvXMLImportContext* pContext = nullptr; + + switch (nElement) + { + case XML_ELEMENT( OFFICE, XML_DOCUMENT ): + case XML_ELEMENT( OFFICE, XML_DOCUMENT_META ): + { + uno::Reference<document::XDocumentPropertiesSupplier> xDPS( + GetModel(), uno::UNO_QUERY); + // mst@: right now, this seems to be not supported, so it is untested + if (xDPS.is()) { + pContext = (nElement == XML_ELEMENT(OFFICE, XML_DOCUMENT_META)) + ? new SvXMLMetaDocumentContext(*this, xDPS->getDocumentProperties()) + // flat OpenDocument file format + : new SchXMLFlatDocContext_Impl(*maImportHelper, *this, nElement, + xDPS->getDocumentProperties()); + } + } + break; + // accept <office:document> + case XML_ELEMENT(OFFICE, XML_DOCUMENT_STYLES): + case XML_ELEMENT(OFFICE, XML_DOCUMENT_CONTENT): + pContext = new SchXMLDocContext(*maImportHelper, *this, nElement); + break; + } + return pContext; +} + +SvXMLImportContext* SchXMLImport::CreateStylesContext() +{ + //#i103287# make sure that the version information is set before importing all the properties (especially stroke-opacity!) + SchXMLTools::setBuildIDAtImportInfo( GetModel(), getImportInfo() ); + + SvXMLStylesContext* pStylesCtxt = new SvXMLStylesContext( *this ); + + // set context at base class, so that all auto-style classes are imported + SetAutoStyles( pStylesCtxt ); + maImportHelper->SetAutoStylesContext( pStylesCtxt ); + + return pStylesCtxt; +} + +void SAL_CALL SchXMLImport::setTargetDocument(const uno::Reference<lang::XComponent>& xDoc) +{ + uno::Reference<chart2::XChartDocument> xOldDoc(GetModel(), uno::UNO_QUERY); + if (xOldDoc.is() && xOldDoc->hasControllersLocked()) + xOldDoc->unlockControllers(); + + SvXMLImport::setTargetDocument(xDoc); + + uno::Reference<chart2::XChartDocument> xChartDoc(GetModel(), uno::UNO_QUERY); + + if (!xChartDoc.is()) + return; + try + { + // prevent rebuild of view during load (necessary especially if loaded not + // via load api, which is the case for example if binary files are loaded) + xChartDoc->lockControllers(); + + uno::Reference<container::XChild> xChild(xChartDoc, uno::UNO_QUERY); + uno::Reference<chart2::data::XDataReceiver> xDataReceiver(xChartDoc, uno::UNO_QUERY); + if (xChild.is() && xDataReceiver.is()) + { + Reference<lang::XMultiServiceFactory> xFact(xChild->getParent(), uno::UNO_QUERY); + if (xFact.is()) + { + //if the parent has a number formatter we will use the numberformatter of the parent + Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(xFact, uno::UNO_QUERY); + xDataReceiver->attachNumberFormatsSupplier(xNumberFormatsSupplier); + } + } + } + catch (const uno::Exception &) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "SchXMLChartContext::StartElement(): Exception caught"); + } +} + +// first version: everything comes from one storage + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisImporter_get_implementation(uno::XComponentContext* pCtx, + uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLImport(pCtx, "SchXMLImport", SvXMLImportFlags::ALL)); +} + +// multiple storage version: one for content / styles / meta + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisMetaImporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLImport(pCtx, "SchXMLImport.Meta", SvXMLImportFlags::META)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisStylesImporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLImport(pCtx, "SchXMLImport.Styles", SvXMLImportFlags::STYLES)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +com_sun_star_comp_Chart_XMLOasisContentImporter_get_implementation( + uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/) +{ + return cppu::acquire(new SchXMLImport(pCtx, "SchXMLImport.Content", + SvXMLImportFlags::CONTENT | SvXMLImportFlags::AUTOSTYLES + | SvXMLImportFlags::FONTDECLS)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLLegendContext.cxx b/xmloff/source/chart/SchXMLLegendContext.cxx new file mode 100644 index 000000000..b9e652b18 --- /dev/null +++ b/xmloff/source/chart/SchXMLLegendContext.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLLegendContext.hxx" +#include "SchXMLEnumConverter.hxx" + +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmluconv.hxx> + +#include <sal/log.hxx> + +#include <com/sun/star/chart/ChartLegendExpansion.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> + +using namespace ::xmloff::token; +using namespace com::sun::star; + +SchXMLLegendContext::SchXMLLegendContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ) +{ +} + +void SchXMLLegendContext::startFastElement( sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument(); + if( !xDoc.is() ) + return; + + // turn on legend + uno::Reference< beans::XPropertySet > xDocProp( xDoc, uno::UNO_QUERY ); + if( xDocProp.is() ) + { + try + { + xDocProp->setPropertyValue("HasLegend", uno::Any( true ) ); + } + catch(const beans::UnknownPropertyException&) + { + SAL_INFO("xmloff.chart", "Property HasLegend not found" ); + } + } + + uno::Reference< drawing::XShape > xLegendShape = xDoc->getLegend(); + uno::Reference< beans::XPropertySet > xLegendProps( xLegendShape, uno::UNO_QUERY ); + if( !xLegendShape.is() || !xLegendProps.is() ) + { + SAL_INFO("xmloff.chart", "legend could not be created" ); + return; + } + + // parse attributes + awt::Point aLegendPos; + bool bOverlay = false; + bool bHasXPosition=false; + bool bHasYPosition=false; + awt::Size aLegendSize; + bool bHasWidth=false; + bool bHasHeight=false; + chart::ChartLegendExpansion nLegendExpansion = chart::ChartLegendExpansion_HIGH; + bool bHasExpansion=false; + + OUString sAutoStyleName; + uno::Any aAny; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(CHART, XML_LEGEND_POSITION): + try + { + if( SchXMLEnumConverter::getLegendPositionConverter().importXML( aIter.toString(), aAny, GetImport().GetMM100UnitConverter() ) ) + xLegendProps->setPropertyValue("Alignment", aAny ); + } + catch(const beans::UnknownPropertyException&) + { + SAL_INFO("xmloff.chart", "Property Alignment (legend) not found" ); + } + break; + case XML_ELEMENT(LO_EXT, XML_OVERLAY): + try + { + bOverlay = aIter.toBoolean(); + xLegendProps->setPropertyValue("Overlay", uno::Any(bOverlay)); + } + catch(const beans::UnknownPropertyException&) + { + SAL_INFO("xmloff.chart", "Property Overlay (legend) not found" ); + } + break; + case XML_ELEMENT(SVG, XML_X): + case XML_ELEMENT(SVG_COMPAT, XML_X): + GetImport().GetMM100UnitConverter().convertMeasureToCore( + aLegendPos.X, aIter.toView() ); + bHasXPosition = true; + break; + case XML_ELEMENT(SVG, XML_Y): + case XML_ELEMENT(SVG_COMPAT, XML_Y): + GetImport().GetMM100UnitConverter().convertMeasureToCore( + aLegendPos.Y, aIter.toView() ); + bHasYPosition = true; + break; + case XML_ELEMENT(CHART, XML_STYLE_NAME): + sAutoStyleName = aIter.toString(); + break; + case XML_ELEMENT(STYLE, XML_LEGEND_EXPANSION): + SchXMLEnumConverter::getLegendPositionConverter().importXML( aIter.toString(), aAny, GetImport().GetMM100UnitConverter() ); + bHasExpansion = (aAny>>=nLegendExpansion); + break; + case XML_ELEMENT(STYLE, XML_LEGEND_EXPANSION_ASPECT_RATIO): + break; + case XML_ELEMENT(SVG, XML_WIDTH): + case XML_ELEMENT(SVG_COMPAT, XML_WIDTH): + case XML_ELEMENT(CHART_EXT, XML_WIDTH): + GetImport().GetMM100UnitConverter().convertMeasureToCore( + aLegendSize.Width, aIter.toView() ); + bHasWidth = true; + break; + case XML_ELEMENT(SVG, XML_HEIGHT): + case XML_ELEMENT(SVG_COMPAT, XML_HEIGHT): + case XML_ELEMENT(CHART_EXT, XML_HEIGHT): + GetImport().GetMM100UnitConverter().convertMeasureToCore( + aLegendSize.Height, aIter.toView() ); + bHasHeight = true; + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + break; + } + } + + if( bHasExpansion && nLegendExpansion!= chart::ChartLegendExpansion_CUSTOM ) + xLegendProps->setPropertyValue("Expansion", uno::Any(nLegendExpansion) ); + else if( bHasHeight && bHasWidth ) + xLegendShape->setSize( aLegendSize ); + + if( bHasXPosition && bHasYPosition ) + xLegendShape->setPosition( aLegendPos ); + + // the fill style has the default "none" in XML, but "solid" in the model. + xLegendProps->setPropertyValue("FillStyle", uno::Any( drawing::FillStyle_NONE )); + + // set auto-styles for Legend + mrImportHelper.FillAutoStyle(sAutoStyleName, xLegendProps); +} + +SchXMLLegendContext::~SchXMLLegendContext() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLLegendContext.hxx b/xmloff/source/chart/SchXMLLegendContext.hxx new file mode 100644 index 000000000..9477e45b7 --- /dev/null +++ b/xmloff/source/chart/SchXMLLegendContext.hxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <com/sun/star/xml/sax/XAttributeList.hpp> + +#include <xmloff/SchXMLImportHelper.hxx> +#include <xmloff/xmlictxt.hxx> + +class SchXMLLegendContext : public SvXMLImportContext +{ +public: + SchXMLLegendContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport ); + virtual ~SchXMLLegendContext() override; + + virtual void SAL_CALL startFastElement( sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + +private: + SchXMLImportHelper& mrImportHelper; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLParagraphContext.cxx b/xmloff/source/chart/SchXMLParagraphContext.cxx new file mode 100644 index 000000000..84e22c5a5 --- /dev/null +++ b/xmloff/source/chart/SchXMLParagraphContext.cxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "SchXMLParagraphContext.hxx" + +#include <sal/log.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/namespacemap.hxx> + +#include <com/sun/star/xml/sax/XAttributeList.hpp> + +using namespace com::sun::star; +using namespace ::xmloff::token; + +SchXMLParagraphContext::SchXMLParagraphContext( SvXMLImport& rImport, + OUString& rText, + OUString * pOutId /* = 0 */ ) : + SvXMLImportContext( rImport ), + mrText( rText ), + mpId( pOutId ) +{ +} + +SchXMLParagraphContext::~SchXMLParagraphContext() +{} + +void SchXMLParagraphContext::startFastElement( + sal_Int32 /*nElement*/, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + // remember the id. It is used for storing the original cell range string in + // a local table (cached data) + if( !mpId ) + return; + + bool bHaveXmlId( false ); + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(XML, XML_ID): + (*mpId) = aIter.toString(); + bHaveXmlId = true; + break; + case XML_ELEMENT(TEXT, XML_ID): + { // text:id shall be ignored if xml:id exists + if (!bHaveXmlId) + { + (*mpId) = aIter.toString(); + } + break; + } + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } +} + +void SchXMLParagraphContext::endFastElement(sal_Int32 ) +{ + mrText = maBuffer.makeStringAndClear(); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLParagraphContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + if( nElement == XML_ELEMENT(TEXT, XML_TAB_STOP) ) + { + maBuffer.append( u'\x0009'); // tabulator + } + else if( nElement == XML_ELEMENT(TEXT, XML_LINE_BREAK) ) + { + maBuffer.append( u'\x000A'); // linefeed + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + + return nullptr; +} + +void SchXMLParagraphContext::characters( const OUString& rChars ) +{ + maBuffer.append( rChars ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLParagraphContext.hxx b/xmloff/source/chart/SchXMLParagraphContext.hxx new file mode 100644 index 000000000..65e90522e --- /dev/null +++ b/xmloff/source/chart/SchXMLParagraphContext.hxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlictxt.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> + +namespace com::sun::star::xml::sax { + class XAttributeList; +} + +class SchXMLParagraphContext : public SvXMLImportContext +{ +private: + OUString& mrText; + OUString* mpId; + OUStringBuffer maBuffer; + +public: + SchXMLParagraphContext( SvXMLImport& rImport, + OUString& rText, + OUString * pOutId = nullptr ); + virtual ~SchXMLParagraphContext() override; + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + virtual void SAL_CALL characters( const OUString& rChars ) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.cxx b/xmloff/source/chart/SchXMLPlotAreaContext.cxx new file mode 100644 index 000000000..7418b92fa --- /dev/null +++ b/xmloff/source/chart/SchXMLPlotAreaContext.cxx @@ -0,0 +1,1280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLPlotAreaContext.hxx" +#include <SchXMLImport.hxx> +#include "SchXMLAxisContext.hxx" +#include "SchXMLSeries2Context.hxx" +#include "SchXMLTools.hxx" + +#include <comphelper/processfactory.hxx> +#include <comphelper/sequence.hxx> +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/prstylei.hxx> +#include <xmloff/xmlstyle.hxx> +#include <oox/helper/containerhelper.hxx> + +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart/X3DDisplay.hpp> +#include <com/sun/star/chart/XStatisticDisplay.hpp> +#include <com/sun/star/chart/XDiagramPositioning.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/data/XDataSink.hpp> +#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp> +#include <com/sun/star/chart2/data/LabeledDataSequence.hpp> +#include <com/sun/star/drawing/CameraGeometry.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/xml/sax/XAttributeList.hpp> + +using namespace com::sun::star; +using namespace ::xmloff::token; + +using com::sun::star::uno::Reference; + +namespace +{ + +struct lcl_AxisHasCategories +{ + bool operator() ( const SchXMLAxis & rAxis ) + { + return rAxis.bHasCategories; + } +}; + +OUString lcl_ConvertRange( const OUString & rRange, const uno::Reference< chart2::XChartDocument > & xDoc ) +{ + OUString aResult = rRange; + if(!xDoc.is()) + return aResult; + uno::Reference< chart2::data::XRangeXMLConversion > xConversion( + xDoc->getDataProvider(), uno::UNO_QUERY ); + if( xConversion.is()) + aResult = xConversion->convertRangeFromXML( rRange ); + return aResult; +} + +} // anonymous namespace + +SchXML3DSceneAttributesHelper::SchXML3DSceneAttributesHelper( SvXMLImport& rImporter ) + : SdXML3DSceneAttributesHelper( rImporter ) +{ +} + +void SchXML3DSceneAttributesHelper::getCameraDefaultFromDiagram( const uno::Reference< chart::XDiagram >& xDiagram ) +{ + //different defaults for camera geometry necessary to workaround wrong behaviour in old chart + //in future make this version dependent if we have versioning (metastream) for ole objects + + try + { + uno::Reference< beans::XPropertySet > xProp( xDiagram, uno::UNO_QUERY ); + if( xProp.is() ) + { + drawing::CameraGeometry aCamGeo; + xProp->getPropertyValue("D3DCameraGeometry") >>= aCamGeo; + maVRP.setX( aCamGeo.vrp.PositionX ); + maVRP.setY( aCamGeo.vrp.PositionY ); + maVRP.setZ( aCamGeo.vrp.PositionZ ); + maVPN.setX( aCamGeo.vpn.DirectionX ); + maVPN.setY( aCamGeo.vpn.DirectionY ); + maVPN.setZ( aCamGeo.vpn.DirectionZ ); + maVUP.setX( aCamGeo.vup.DirectionX ); + maVUP.setY( aCamGeo.vup.DirectionY ); + maVUP.setZ( aCamGeo.vup.DirectionZ ); + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught for property NumberOfLines"); + } +} + +SchXML3DSceneAttributesHelper::~SchXML3DSceneAttributesHelper() +{ +} + +SchXMLPlotAreaContext::SchXMLPlotAreaContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + const OUString& rXLinkHRefAttributeToIndicateDataProvider, + OUString& rCategoriesAddress, + OUString& rChartAddress, + bool & rbHasRangeAtPlotArea, + bool & rAllRangeAddressesAvailable, + bool & rColHasLabels, + bool & rRowHasLabels, + chart::ChartDataRowSource & rDataRowSource, + SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles, + const OUString& aChartTypeServiceName, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + const awt::Size & rChartSize ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mrCategoriesAddress( rCategoriesAddress ), + mrSeriesDefaultsAndStyles( rSeriesDefaultsAndStyles ), + mnNumOfLinesProp( 0 ), + mbStockHasVolume( false ), + mnSeries( 0 ), + m_aGlobalSeriesImportInfo( rAllRangeAddressesAvailable ), + maSceneImportHelper( rImport ), + m_aOuterPositioning( rImport ), + m_aInnerPositioning( rImport ), + mbPercentStacked(false), + m_bAxisPositionAttributeImported(false), + m_rXLinkHRefAttributeToIndicateDataProvider(rXLinkHRefAttributeToIndicateDataProvider), + mrChartAddress( rChartAddress ), + m_rbHasRangeAtPlotArea( rbHasRangeAtPlotArea ), + mrColHasLabels( rColHasLabels ), + mrRowHasLabels( rRowHasLabels ), + mrDataRowSource( rDataRowSource ), + maChartTypeServiceName( aChartTypeServiceName ), + mrLSequencesPerIndex( rLSequencesPerIndex ), + mbGlobalChartTypeUsedBySeries( false ), + maChartSize( rChartSize ) +{ + m_rbHasRangeAtPlotArea = false; + + // get Diagram + uno::Reference< chart::XChartDocument > xDoc = rImpHelper.GetChartDocument(); + if( xDoc.is()) + { + mxDiagram = xDoc->getDiagram(); + mxNewDoc.set( xDoc, uno::UNO_QUERY ); + + maSceneImportHelper.getCameraDefaultFromDiagram( mxDiagram ); + } + SAL_WARN_IF( !mxDiagram.is(),"xmloff.chart", "Couldn't get XDiagram" ); + + // turn off all axes initially + uno::Any aFalseBool; + aFalseBool <<= false; + + uno::Reference< lang::XServiceInfo > xInfo( mxDiagram, uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xProp( mxDiagram, uno::UNO_QUERY ); + if( !xInfo.is() || !xProp.is() ) + return; + + try + { + xProp->setPropertyValue("HasXAxis", aFalseBool ); + xProp->setPropertyValue("HasXAxisGrid", aFalseBool ); + xProp->setPropertyValue("HasXAxisDescription", aFalseBool ); + xProp->setPropertyValue("HasSecondaryXAxis", aFalseBool ); + xProp->setPropertyValue("HasSecondaryXAxisDescription", aFalseBool ); + + xProp->setPropertyValue("HasYAxis", aFalseBool ); + xProp->setPropertyValue("HasYAxisGrid", aFalseBool ); + xProp->setPropertyValue("HasYAxisDescription", aFalseBool ); + xProp->setPropertyValue("HasSecondaryYAxis", aFalseBool ); + xProp->setPropertyValue("HasSecondaryYAxisDescription", aFalseBool ); + + xProp->setPropertyValue("HasZAxis", aFalseBool ); + xProp->setPropertyValue("HasZAxisDescription", aFalseBool ); + + xProp->setPropertyValue("DataRowSource", uno::Any(chart::ChartDataRowSource_COLUMNS) ); + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Property required by service not supported" ); + } +} + +SchXMLPlotAreaContext::~SchXMLPlotAreaContext() +{} + +void SchXMLPlotAreaContext::startFastElement (sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + // parse attributes + uno::Reference< chart2::XChartDocument > xNewDoc( GetImport().GetModel(), uno::UNO_QUERY ); + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch( aIter.getToken() ) + { + case XML_ELEMENT(SVG, XML_X): + case XML_ELEMENT(SVG_COMPAT, XML_X): + case XML_ELEMENT(SVG, XML_Y): + case XML_ELEMENT(SVG_COMPAT, XML_Y): + case XML_ELEMENT(SVG, XML_WIDTH): + case XML_ELEMENT(SVG_COMPAT, XML_WIDTH): + case XML_ELEMENT(SVG, XML_HEIGHT): + case XML_ELEMENT(SVG_COMPAT, XML_HEIGHT): + m_aOuterPositioning.readPositioningAttribute( aIter.getToken(), aIter.toView() ); + break; + case XML_ELEMENT(CHART, XML_STYLE_NAME): + msAutoStyleName = aIter.toString(); + break; + case XML_ELEMENT(TABLE, XML_CELL_RANGE_ADDRESS): + mrChartAddress = lcl_ConvertRange( aIter.toString(), xNewDoc ); + // indicator for getting data from the outside + m_rbHasRangeAtPlotArea = true; + break; + case XML_ELEMENT(CHART, XML_DATA_SOURCE_HAS_LABELS): + { + if( IsXMLToken(aIter, XML_BOTH) ) + mrColHasLabels = mrRowHasLabels = true; + else if( IsXMLToken(aIter, XML_ROW) ) + mrRowHasLabels = true; + else if( IsXMLToken(aIter, XML_COLUMN) ) + mrColHasLabels = true; + } + break; + case XML_ELEMENT(DR3D, XML_TRANSFORM): + case XML_ELEMENT(DR3D, XML_VRP): + case XML_ELEMENT(DR3D, XML_VPN): + case XML_ELEMENT(DR3D, XML_VUP): + case XML_ELEMENT(DR3D, XML_PROJECTION): + case XML_ELEMENT(DR3D, XML_DISTANCE): + case XML_ELEMENT(DR3D, XML_FOCAL_LENGTH): + case XML_ELEMENT(DR3D, XML_SHADOW_SLANT): + case XML_ELEMENT(DR3D, XML_SHADE_MODE): + case XML_ELEMENT(DR3D, XML_AMBIENT_COLOR): + case XML_ELEMENT(DR3D, XML_LIGHTING_MODE): + maSceneImportHelper.processSceneAttribute( aIter ); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + if( ! mxNewDoc.is()) + { + uno::Reference< beans::XPropertySet > xDocProp( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ); + if( xDocProp.is()) + { + try + { + xDocProp->setPropertyValue("DataSourceLabelsInFirstColumn", uno::Any(mrColHasLabels) ); + xDocProp->setPropertyValue("DataSourceLabelsInFirstRow", uno::Any(mrRowHasLabels) ); + } + catch( const beans::UnknownPropertyException & ) + { + SAL_WARN("xmloff.chart", "Properties missing" ); + } + } + } + + // set properties + uno::Reference< beans::XPropertySet > xProp( mxDiagram, uno::UNO_QUERY ); + if( !msAutoStyleName.isEmpty()) + { + if( xProp.is()) + { + const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext(); + if( pStylesCtxt ) + { + const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), msAutoStyleName ); + + XMLPropStyleContext* pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( pStyle ) ); + if( pPropStyleContext ) + { + pPropStyleContext->FillPropertySet( xProp ); + + // get the data row source that was set without having data + xProp->getPropertyValue("DataRowSource") + >>= mrDataRowSource; + + //lines on/off + //this old property is not supported fully anymore with the new chart, so we need to get the information a little bit different from similar properties + mrSeriesDefaultsAndStyles.maLinesOnProperty = SchXMLTools::getPropertyFromContext( + u"Lines", pPropStyleContext, pStylesCtxt ); + + //handle automatic position and size + m_aOuterPositioning.readAutomaticPositioningProperties( pPropStyleContext, pStylesCtxt ); + + //correct default starting angle for old 3D pies + if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan3_0( GetImport().GetModel() ) ) + { + bool bIs3d = false; + if( xProp.is() && ( xProp->getPropertyValue("Dim3D") >>= bIs3d ) && + bIs3d ) + { + if( maChartTypeServiceName == "com.sun.star.chart2.PieChartType" || maChartTypeServiceName == "com.sun.star.chart2.DonutChartType" ) + { + OUString aPropName( "StartingAngle" ); + uno::Any aAStartingAngle( SchXMLTools::getPropertyFromContext( aPropName, pPropStyleContext, pStylesCtxt ) ); + if( !aAStartingAngle.hasValue() ) + xProp->setPropertyValue( aPropName, uno::Any(sal_Int32(0)) ) ; + } + } + } + } + } + } + } + + //remember default values for dataseries + if(xProp.is()) + { + try + { + mrSeriesDefaultsAndStyles.maSymbolTypeDefault = xProp->getPropertyValue("SymbolType"); + mrSeriesDefaultsAndStyles.maDataCaptionDefault = xProp->getPropertyValue("DataCaption"); + + mrSeriesDefaultsAndStyles.maMeanValueDefault = xProp->getPropertyValue("MeanValue"); + mrSeriesDefaultsAndStyles.maRegressionCurvesDefault = xProp->getPropertyValue("RegressionCurves"); + + bool bStacked = false; + mrSeriesDefaultsAndStyles.maStackedDefault = xProp->getPropertyValue("Stacked"); + mrSeriesDefaultsAndStyles.maStackedDefault >>= bStacked; + mrSeriesDefaultsAndStyles.maPercentDefault = xProp->getPropertyValue("Percent"); + mrSeriesDefaultsAndStyles.maPercentDefault >>= mbPercentStacked; + mrSeriesDefaultsAndStyles.maStackedBarsConnectedDefault = xProp->getPropertyValue("StackedBarsConnected"); + + // deep + uno::Any aDeepProperty( xProp->getPropertyValue("Deep")); + // #124488# old versions store a 3d area and 3D line deep chart with Deep==false => workaround for this + if( ! (bStacked || mbPercentStacked )) + { + if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( GetImport().GetModel() ) ) + { + bool bIs3d = false; + if( ( xProp->getPropertyValue("Dim3D") >>= bIs3d ) && + bIs3d ) + { + if( maChartTypeServiceName == "com.sun.star.chart2.AreaChartType" || maChartTypeServiceName == "com.sun.star.chart2.LineChartType" ) + { + aDeepProperty <<= true; + } + } + } + } + mrSeriesDefaultsAndStyles.maDeepDefault = aDeepProperty; + + xProp->getPropertyValue("NumberOfLines") >>= mnNumOfLinesProp; + xProp->getPropertyValue("Volume") >>= mbStockHasVolume; + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "PlotAreaContext:EndElement(): Exception caught"); + } + } // if + + bool bCreateInternalDataProvider = false; + if( m_rXLinkHRefAttributeToIndicateDataProvider == "." ) //data comes from the chart itself + bCreateInternalDataProvider = true; + else if( m_rXLinkHRefAttributeToIndicateDataProvider == ".." ) //data comes from the parent application + bCreateInternalDataProvider = false; + else if( !m_rXLinkHRefAttributeToIndicateDataProvider.isEmpty() ) //not supported so far to get the data by sibling objects -> fall back to chart itself + bCreateInternalDataProvider = true; + else if( !m_rbHasRangeAtPlotArea ) + bCreateInternalDataProvider = true; + + if( bCreateInternalDataProvider && mxNewDoc.is() ) + { + // we have no complete range => we have own data, so switch the data + // provider to internal. Clone is not necessary, as we don't have any + // data yet. + mxNewDoc->createInternalDataProvider( false /* bCloneExistingData */ ); + if( xProp.is() && mrDataRowSource!=chart::ChartDataRowSource_COLUMNS ) + xProp->setPropertyValue("DataRowSource", uno::Any(mrDataRowSource) ); + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLPlotAreaContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + SvXMLImportContext* pContext = nullptr; + + switch(nElement) + { + case XML_ELEMENT(CHART_EXT, XML_COORDINATE_REGION): + case XML_ELEMENT(CHART, XML_COORDINATE_REGION): + { + pContext = new SchXMLCoordinateRegionContext( GetImport(), m_aInnerPositioning ); + } + break; + + case XML_ELEMENT(CHART, XML_AXIS): + { + bool bAddMissingXAxisForNetCharts = false; + bool bAdaptWrongPercentScaleValues = false; + if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( GetImport().GetModel() ) ) + { + //correct errors from older versions + + // for NetCharts there were no xAxis exported to older files + // so we need to add the x axis here for those old NetChart files + if ( maChartTypeServiceName == "com.sun.star.chart2.NetChartType" ) + bAddMissingXAxisForNetCharts = true; + + //Issue 59288 + if( mbPercentStacked ) + bAdaptWrongPercentScaleValues = true; + } + + bool bAdaptXAxisOrientationForOld2DBarCharts = false; + if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_4( GetImport().GetModel() ) ) + { + //issue74660 + if ( maChartTypeServiceName == "com.sun.star.chart2.ColumnChartType" ) + bAdaptXAxisOrientationForOld2DBarCharts = true; + } + + pContext = new SchXMLAxisContext( mrImportHelper, GetImport(), mxDiagram, maAxes, mrCategoriesAddress, + bAddMissingXAxisForNetCharts, bAdaptWrongPercentScaleValues, bAdaptXAxisOrientationForOld2DBarCharts, m_bAxisPositionAttributeImported ); + } + break; + + case XML_ELEMENT(CHART, XML_SERIES): + { + if( mxNewDoc.is()) + { + pContext = new SchXMLSeries2Context( + mrImportHelper, GetImport(), + mxNewDoc, maAxes, + mrSeriesDefaultsAndStyles.maSeriesStyleVector, + mrSeriesDefaultsAndStyles.maRegressionStyleVector, + mnSeries, + mbStockHasVolume, + m_aGlobalSeriesImportInfo, + maChartTypeServiceName, + mrLSequencesPerIndex, + mbGlobalChartTypeUsedBySeries, maChartSize ); + } + mnSeries++; + } + break; + + case XML_ELEMENT(CHART, XML_WALL): + pContext = new SchXMLWallFloorContext( mrImportHelper, GetImport(), mxDiagram, + SchXMLWallFloorContext::CONTEXT_TYPE_WALL ); + break; + case XML_ELEMENT(CHART, XML_FLOOR): + pContext = new SchXMLWallFloorContext( mrImportHelper, GetImport(), mxDiagram, + SchXMLWallFloorContext::CONTEXT_TYPE_FLOOR ); + break; + + case XML_ELEMENT(DR3D, XML_LIGHT): + pContext = maSceneImportHelper.create3DLightContext( xAttrList ); + break; + + // elements for stock charts + case XML_ELEMENT(CHART, XML_STOCK_GAIN_MARKER): + pContext = new SchXMLStockContext( mrImportHelper, GetImport(), mxDiagram, + SchXMLStockContext::CONTEXT_TYPE_GAIN ); + break; + case XML_ELEMENT(CHART, XML_STOCK_LOSS_MARKER): + pContext = new SchXMLStockContext( mrImportHelper, GetImport(), mxDiagram, + SchXMLStockContext::CONTEXT_TYPE_LOSS ); + break; + case XML_ELEMENT(CHART, XML_STOCK_RANGE_LINE): + pContext = new SchXMLStockContext( mrImportHelper, GetImport(), mxDiagram, + SchXMLStockContext::CONTEXT_TYPE_RANGE ); + break; + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + } + + return pContext; +} + +void SchXMLPlotAreaContext::endFastElement(sal_Int32 ) +{ + // set categories + if( !mrCategoriesAddress.isEmpty() && mxNewDoc.is()) + { + uno::Reference< chart2::data::XDataProvider > xDataProvider( + mxNewDoc->getDataProvider() ); + // @todo: correct coordinate system index + sal_Int32 nDimension( 0 ); + ::std::vector< SchXMLAxis >::const_iterator aIt( + ::std::find_if( maAxes.begin(), maAxes.end(), lcl_AxisHasCategories())); + if( aIt != maAxes.end()) + nDimension = static_cast< sal_Int32 >( (*aIt).eDimension ); + SchXMLTools::CreateCategories( + xDataProvider, mxNewDoc, mrCategoriesAddress, + 0 /* nCooSysIndex */, + nDimension, &mrLSequencesPerIndex ); + } + + uno::Reference< beans::XPropertySet > xDiaProp( mxDiagram, uno::UNO_QUERY ); + if( xDiaProp.is()) + { + bool bIsThreeDim = false; + uno::Any aAny = xDiaProp->getPropertyValue("Dim3D"); + aAny >>= bIsThreeDim; + + // set 3d scene attributes + if( bIsThreeDim ) + { + // set scene attributes at diagram + maSceneImportHelper.setSceneAttributes( xDiaProp ); + } + + // set correct number of lines at series + if( ! m_aGlobalSeriesImportInfo.rbAllRangeAddressesAvailable && mnNumOfLinesProp > 0 && maChartTypeServiceName == "com.sun.star.chart2.ColumnChartType" ) + { + try + { + xDiaProp->setPropertyValue("NumberOfLines", + uno::Any( mnNumOfLinesProp )); + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught for property NumberOfLines"); + } + } + + // #i32366# stock has volume + if( mxDiagram->getDiagramType() == "com.sun.star.chart.StockDiagram" && + mbStockHasVolume ) + { + try + { + xDiaProp->setPropertyValue("Volume", + uno::Any( true )); + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught for property Volume"); + } + } + } + + // set changed size and position after properties (esp. 3d) + + uno::Reference< chart::XDiagramPositioning > xDiaPos( mxDiagram, uno::UNO_QUERY ); + if( xDiaPos.is()) + { + if( !m_aOuterPositioning.isAutomatic() ) + { + if( m_aInnerPositioning.hasPosSize() ) + xDiaPos->setDiagramPositionExcludingAxes( m_aInnerPositioning.getRectangle() ); + else if( m_aOuterPositioning.hasPosSize() ) + { + if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan3_3( GetImport().GetModel() ) ) //old version of OOo did write a wrong rectangle for the diagram size + xDiaPos->setDiagramPositionIncludingAxesAndAxisTitles( m_aOuterPositioning.getRectangle() ); + else + xDiaPos->setDiagramPositionIncludingAxes( m_aOuterPositioning.getRectangle() ); + } + } + } + + SchXMLAxisContext::CorrectAxisPositions( uno::Reference< chart2::XChartDocument >( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ), maChartTypeServiceName, GetImport().GetODFVersion(), m_bAxisPositionAttributeImported ); +} + +SchXMLDataLabelSpanContext::SchXMLDataLabelSpanContext( SvXMLImport& rImport, ::std::vector<OUString>& rLabels): + SvXMLImportContext( rImport ), + mrLabels(rLabels) +{ +} + +void SchXMLDataLabelSpanContext::characters(const OUString& rChars) +{ + maCharBuffer.append(rChars); +} + +void SchXMLDataLabelSpanContext::endFastElement(sal_Int32 ) +{ + mrLabels.push_back(maCharBuffer.makeStringAndClear()); +} + +SchXMLDataLabelParaContext::SchXMLDataLabelParaContext( SvXMLImport& rImport, ::std::vector<OUString>& rLabels): + SvXMLImportContext( rImport ), + mrLabels(rLabels) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLDataLabelParaContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + if ( nElement == XML_ELEMENT(TEXT, XML_SPAN) ) + return new SchXMLDataLabelSpanContext(GetImport(), mrLabels); + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return nullptr; +} + +SchXMLDataLabelContext::SchXMLDataLabelContext(SvXMLImport& rImport, + CustomLabelsInfo& rLabels, + DataRowPointStyle& rDataLabelStyle) + : SvXMLImportContext(rImport) + , mrLabels(rLabels) + , mrDataLabelStyle(rDataLabelStyle) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLDataLabelContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + if ( nElement == XML_ELEMENT(TEXT, XML_P) ) + return new SchXMLDataLabelParaContext(GetImport(), mrLabels.mLabels); + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return nullptr; +} + +void SchXMLDataLabelContext::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(SVG, XML_X): + case XML_ELEMENT(SVG_COMPAT, XML_X): + { + sal_Int32 nResultValue; + GetImport().GetMM100UnitConverter().convertMeasureToCore(nResultValue, aIter.toView()); + mrDataLabelStyle.mo_nLabelAbsolutePosX = nResultValue; + break; + } + case XML_ELEMENT(SVG, XML_Y): + case XML_ELEMENT(SVG_COMPAT, XML_Y): + { + sal_Int32 nResultValue; + GetImport().GetMM100UnitConverter().convertMeasureToCore(nResultValue, aIter.toView()); + mrDataLabelStyle.mo_nLabelAbsolutePosY = nResultValue; + break; + } + case XML_ELEMENT(CHART, XML_STYLE_NAME): + mrDataLabelStyle.msStyleName = aIter.toString(); + break; + case XML_ELEMENT(LO_EXT, XML_DATA_LABEL_GUID): + mrLabels.msLabelGuid = aIter.toString(); + mrLabels.mbDataLabelsRange = true; + break; + case XML_ELEMENT(LO_EXT, XML_DATA_LABELS_CELL_RANGE): + mrLabels.msLabelsCellRange = aIter.toString(); + mrLabels.mbDataLabelsRange = true; + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } +} + + +SchXMLDataPointContext::SchXMLDataPointContext( SvXMLImport& rImport, + ::std::vector< DataRowPointStyle >& rStyleVector, + const css::uno::Reference< css::chart2::XDataSeries >& xSeries, + sal_Int32& rIndex, + bool bSymbolSizeForSeriesIsMissingInFile ) : + SvXMLImportContext( rImport ), + mrStyleVector( rStyleVector ), + mrIndex( rIndex ), + mDataPoint(DataRowPointStyle::DATA_POINT, xSeries, rIndex, 1, OUString{}), + mDataLabel(DataRowPointStyle::DATA_LABEL_POINT, xSeries, rIndex, 1, OUString{}) +{ + mDataPoint.mbSymbolSizeForSeriesIsMissingInFile = bSymbolSizeForSeriesIsMissingInFile; +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLDataPointContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + switch(nElement) + { + case XML_ELEMENT(CHART, XML_DATA_LABEL): + mbHasLabelParagraph = true; + pContext = new SchXMLDataLabelContext(GetImport(), mDataPoint.mCustomLabels, + mDataLabel); + break; + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + } + return pContext; +} + +SchXMLDataPointContext::~SchXMLDataPointContext() +{ +} + +void SchXMLDataPointContext::startFastElement (sal_Int32 /*Element*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + OUString sAutoStyleName; + sal_Int32 nRepeat = 1; + OUString sCustomLabelField; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(CHART, XML_STYLE_NAME): + { + sAutoStyleName = aIter.toString(); + mDataPoint.msStyleName = sAutoStyleName; + mDataLabel.msStyleNameOfParent = sAutoStyleName; + break; + } + case XML_ELEMENT(CHART, XML_REPEATED): + { + nRepeat = aIter.toInt32(); + mDataPoint.m_nPointRepeat = nRepeat; + mDataLabel.m_nPointRepeat = nRepeat; + break; + } + // Deprecated. New documents use the chart:data-label element + // instead in order to store custom label text. + case XML_ELEMENT(LO_EXT, XML_CUSTOM_LABEL_FIELD): + if (!mbHasLabelParagraph) + { + sCustomLabelField = aIter.toString(); + mDataPoint.mCustomLabels.mLabels.push_back(sCustomLabelField); + } + break; + case XML_ELEMENT(LO_EXT, XML_HIDE_LEGEND): + { + bool bHideLegend = aIter.toBoolean(); + if (bHideLegend) + { + uno::Sequence<sal_Int32> deletedLegendEntriesSeq; + Reference<beans::XPropertySet> xSeriesProp(mDataPoint.m_xSeries, uno::UNO_QUERY); + xSeriesProp->getPropertyValue("DeletedLegendEntries") >>= deletedLegendEntriesSeq; + std::vector<sal_Int32> deletedLegendEntries; + for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq)) + { + deletedLegendEntries.push_back(deletedLegendEntry); + } + deletedLegendEntries.push_back(mDataPoint.m_nPointIndex); + xSeriesProp->setPropertyValue("DeletedLegendEntries", uno::Any(comphelper::containerToSequence(deletedLegendEntries))); + } + break; + } + case XML_ELEMENT(LO_EXT, XML_CUSTOM_LABEL_POS_X): + { + mDataPoint.mCustomLabelPos[0] = aIter.toDouble(); + break; + } + case XML_ELEMENT(LO_EXT, XML_CUSTOM_LABEL_POS_Y): + { + mDataPoint.mCustomLabelPos[1] = aIter.toDouble(); + break; + } + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + mrIndex += nRepeat; +} + +void SchXMLDataPointContext::endFastElement(sal_Int32 ) +{ + if(!mDataPoint.msStyleName.isEmpty() || mDataPoint.mCustomLabels.mLabels.size() > 0) + { + mrStyleVector.push_back(mDataPoint); + } + if (!mDataLabel.msStyleName.isEmpty() || mDataLabel.mo_nLabelAbsolutePosX.has_value() + || mDataLabel.mo_nLabelAbsolutePosY.has_value()) + { + mrStyleVector.push_back(mDataLabel); + } +} + +SchXMLPositionAttributesHelper::SchXMLPositionAttributesHelper( SvXMLImport& rImporter ) + : m_rImport( rImporter ) + , m_aPosition(0,0) + , m_aSize(0,0) + , m_bHasSizeWidth( false ) + , m_bHasSizeHeight( false ) + , m_bHasPositionX( false ) + , m_bHasPositionY( false ) + , m_bAutoSize( false ) + , m_bAutoPosition( false ) +{ +} + +bool SchXMLPositionAttributesHelper::hasPosSize() const +{ + return (m_bHasPositionX && m_bHasPositionY) && (m_bHasSizeWidth && m_bHasSizeHeight); +} + +bool SchXMLPositionAttributesHelper::isAutomatic() const +{ + return m_bAutoSize || m_bAutoPosition; +} + +void SchXMLPositionAttributesHelper::readPositioningAttribute( sal_Int32 nAttributeToken, std::string_view rValue ) +{ + if( !IsTokenInNamespace(nAttributeToken, XML_NAMESPACE_SVG) && !IsTokenInNamespace(nAttributeToken, XML_NAMESPACE_SVG_COMPAT) ) + return; + + switch (nAttributeToken & TOKEN_MASK) + { + case XML_X: + { + m_rImport.GetMM100UnitConverter().convertMeasureToCore( + m_aPosition.X, rValue ); + m_bHasPositionX = true; + break; + } + case XML_Y: + { + m_rImport.GetMM100UnitConverter().convertMeasureToCore( + m_aPosition.Y, rValue ); + m_bHasPositionY = true; + break; + } + case XML_WIDTH: + { + m_rImport.GetMM100UnitConverter().convertMeasureToCore( + m_aSize.Width, rValue ); + m_bHasSizeWidth = true; + break; + } + case XML_HEIGHT: + { + m_rImport.GetMM100UnitConverter().convertMeasureToCore( + m_aSize.Height, rValue ); + m_bHasSizeHeight = true; + break; + } + default: + XMLOFF_WARN_UNKNOWN_ATTR("xmloff", nAttributeToken, OUString::fromUtf8(rValue)); + } +} + +void SchXMLPositionAttributesHelper::readAutomaticPositioningProperties( XMLPropStyleContext const * pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ) +{ + if( pPropStyleContext && pStylesCtxt ) + { + //handle automatic position and size + SchXMLTools::getPropertyFromContext( + u"AutomaticSize", pPropStyleContext, pStylesCtxt ) >>= m_bAutoSize; + SchXMLTools::getPropertyFromContext( + u"AutomaticPosition", pPropStyleContext, pStylesCtxt ) >>= m_bAutoPosition; + } +} + +SchXMLCoordinateRegionContext::SchXMLCoordinateRegionContext( + SvXMLImport& rImport + , SchXMLPositionAttributesHelper& rPositioning ) + : SvXMLImportContext( rImport ) + , m_rPositioning( rPositioning ) +{ +} + +SchXMLCoordinateRegionContext::~SchXMLCoordinateRegionContext() +{ +} + +void SchXMLCoordinateRegionContext::startFastElement (sal_Int32 /*Element*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + // parse attributes + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + m_rPositioning.readPositioningAttribute( aIter.getToken(), aIter.toView() ); +} + +SchXMLWallFloorContext::SchXMLWallFloorContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + uno::Reference< chart::XDiagram > const & xDiagram, + ContextType eContextType ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mxWallFloorSupplier( xDiagram, uno::UNO_QUERY ), + meContextType( eContextType ) +{ +} + +SchXMLWallFloorContext::~SchXMLWallFloorContext() +{ +} + +void SchXMLWallFloorContext::startFastElement (sal_Int32 /*Element*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + if( !mxWallFloorSupplier.is()) + return; + + OUString sAutoStyleName; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if( aIter.getToken() == XML_ELEMENT(CHART, XML_STYLE_NAME) ) + sAutoStyleName = aIter.toString(); + else + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + + // set properties + uno::Reference< beans::XPropertySet > xProp = ( meContextType == CONTEXT_TYPE_WALL ) + ? mxWallFloorSupplier->getWall() + : mxWallFloorSupplier->getFloor(); + + if (!sAutoStyleName.isEmpty()) + mrImportHelper.FillAutoStyle(sAutoStyleName, xProp); +} + +SchXMLStockContext::SchXMLStockContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + uno::Reference< chart::XDiagram > const & xDiagram, + ContextType eContextType ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mxStockPropProvider( xDiagram, uno::UNO_QUERY ), + meContextType( eContextType ) +{ +} + +SchXMLStockContext::~SchXMLStockContext() +{ +} + +void SchXMLStockContext::startFastElement (sal_Int32 /*Element*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + if( !mxStockPropProvider.is()) + return; + + OUString sAutoStyleName; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if( aIter.getToken() == XML_ELEMENT(CHART, XML_STYLE_NAME) ) + sAutoStyleName = aIter.toString(); + else + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + + if( sAutoStyleName.isEmpty()) + return; + + // set properties + uno::Reference< beans::XPropertySet > xProp; + switch( meContextType ) + { + case CONTEXT_TYPE_GAIN: + xProp = mxStockPropProvider->getUpBar(); + break; + case CONTEXT_TYPE_LOSS: + xProp = mxStockPropProvider->getDownBar(); + break; + case CONTEXT_TYPE_RANGE: + xProp = mxStockPropProvider->getMinMaxLine(); + break; + } + + mrImportHelper.FillAutoStyle(sAutoStyleName, xProp); +} + +static void lcl_setErrorBarSequence ( const uno::Reference< chart2::XChartDocument > &xDoc, + const uno::Reference< beans::XPropertySet > &xBarProp, + const OUString &aXMLRange, + bool bPositiveValue, bool bYError, + tSchXMLLSequencesPerIndex& rSequences) +{ + uno::Reference< css::chart2::data::XDataProvider > xDataProvider(xDoc->getDataProvider()); + uno::Reference< css::chart2::data::XDataSource > xDataSource( xBarProp, uno::UNO_QUERY ); + uno::Reference< css::chart2::data::XDataSink > xDataSink( xDataSource, uno::UNO_QUERY ); + + assert( xDataSink.is() && xDataSource.is() && xDataProvider.is() ); + + OUString aRange(lcl_ConvertRange(aXMLRange,xDoc)); + + uno::Reference< chart2::data::XDataSequence > xNewSequence( + xDataProvider->createDataSequenceByRangeRepresentation( aRange )); + + if( !xNewSequence.is()) + return; + + SchXMLTools::setXMLRangePropertyAtDataSequence(xNewSequence,aXMLRange); + + OUStringBuffer aRoleBuffer("error-bars-"); + if( bYError ) + aRoleBuffer.append( 'y' ); + else + aRoleBuffer.append( 'x'); + + aRoleBuffer.append( '-' ); + + if( bPositiveValue ) + aRoleBuffer = aRoleBuffer.append( "positive" ); + else + aRoleBuffer = aRoleBuffer.append( "negative" ); + + OUString aRole = aRoleBuffer.makeStringAndClear(); + + Reference< beans::XPropertySet > xSeqProp( xNewSequence, uno::UNO_QUERY ); + + xSeqProp->setPropertyValue("Role", uno::Any( aRole )); + + Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + + Reference< chart2::data::XLabeledDataSequence > xLabelSeq( chart2::data::LabeledDataSequence::create(xContext), + uno::UNO_QUERY_THROW ); + + rSequences.emplace( tSchXMLIndexWithPart( -2, SCH_XML_PART_ERROR_BARS ), xLabelSeq ); + + xLabelSeq->setValues( xNewSequence ); + + uno::Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( + xDataSource->getDataSequences()); + + aSequences.realloc( aSequences.getLength() + 1 ); + aSequences.getArray()[ aSequences.getLength() - 1 ] = xLabelSeq; + xDataSink->setData( aSequences ); + +} + +SchXMLStatisticsObjectContext::SchXMLStatisticsObjectContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + const OUString &rSeriesStyleName, + ::std::vector< DataRowPointStyle >& rStyleVector, + const css::uno::Reference< css::chart2::XDataSeries >& xSeries, + ContextType eContextType, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex) : + + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mrStyleVector( rStyleVector ), + m_xSeries( xSeries ), + meContextType( eContextType ), + maSeriesStyleName( rSeriesStyleName), + mrLSequencesPerIndex(rLSequencesPerIndex) +{} + +SchXMLStatisticsObjectContext::~SchXMLStatisticsObjectContext() +{ +} + +namespace { + +void SetErrorBarStyleProperties( const OUString& rStyleName, const uno::Reference< beans::XPropertySet >& xBarProp, + SchXMLImportHelper const & rImportHelper ) +{ + const SvXMLStylesContext* pStylesCtxt = rImportHelper.GetAutoStylesContext(); + const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext(SchXMLImportHelper::GetChartFamilyID(), + rStyleName); + + XMLPropStyleContext &rSeriesStyleContext = + const_cast< XMLPropStyleContext& >( dynamic_cast< const XMLPropStyleContext& >( *pStyle )); + + rSeriesStyleContext.FillPropertySet( xBarProp ); +} + +void SetErrorBarPropertiesFromStyleName( const OUString& aStyleName, const uno::Reference< beans::XPropertySet>& xBarProp, + SchXMLImportHelper const & rImportHelper, OUString& aPosRange, OUString& aNegRange) +{ + const SvXMLStylesContext* pStylesCtxt = rImportHelper.GetAutoStylesContext(); + const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext(SchXMLImportHelper::GetChartFamilyID(), + aStyleName); + + XMLPropStyleContext * pSeriesStyleContext = + const_cast< XMLPropStyleContext * >( dynamic_cast< const XMLPropStyleContext * >( pStyle )); + + uno::Any aAny = SchXMLTools::getPropertyFromContext(u"ErrorBarStyle", + pSeriesStyleContext,pStylesCtxt); + + if ( !aAny.hasValue() ) + return; + + sal_Int32 aBarStyle = css::chart::ErrorBarStyle::NONE; + aAny >>= aBarStyle; + xBarProp->setPropertyValue("ErrorBarStyle", aAny); + + aAny = SchXMLTools::getPropertyFromContext(u"ShowPositiveError", + pSeriesStyleContext,pStylesCtxt); + + if(aAny.hasValue()) + xBarProp->setPropertyValue("ShowPositiveError",aAny); + + aAny = SchXMLTools::getPropertyFromContext(u"ShowNegativeError", + pSeriesStyleContext,pStylesCtxt); + + if(aAny.hasValue()) + xBarProp->setPropertyValue("ShowNegativeError",aAny); + + aAny = SchXMLTools::getPropertyFromContext(u"PositiveError", + pSeriesStyleContext, pStylesCtxt); + + if(aAny.hasValue()) + xBarProp->setPropertyValue("PositiveError", aAny); + else + { + aAny = SchXMLTools::getPropertyFromContext(u"ConstantErrorHigh", + pSeriesStyleContext, pStylesCtxt); + + if(aAny.hasValue()) + xBarProp->setPropertyValue("PositiveError", aAny); + } + + aAny = SchXMLTools::getPropertyFromContext(u"NegativeError", + pSeriesStyleContext, pStylesCtxt); + + if(aAny.hasValue()) + xBarProp->setPropertyValue("NegativeError", aAny); + else + { + aAny = SchXMLTools::getPropertyFromContext(u"ConstantErrorLow", + pSeriesStyleContext, pStylesCtxt); + + if(aAny.hasValue()) + xBarProp->setPropertyValue("NegativeError", aAny); + } + + aAny = SchXMLTools::getPropertyFromContext(u"ErrorBarRangePositive", + pSeriesStyleContext, pStylesCtxt); + if( aAny.hasValue() ) + { + aAny >>= aPosRange; + } + + aAny = SchXMLTools::getPropertyFromContext(u"ErrorBarRangeNegative", + pSeriesStyleContext, pStylesCtxt); + if( aAny.hasValue() ) + { + aAny >>= aNegRange; + } + + aAny = SchXMLTools::getPropertyFromContext(u"Weight", + pSeriesStyleContext, pStylesCtxt); + if( aAny.hasValue() ) + { + xBarProp->setPropertyValue("Weight", aAny); + } + + aAny = SchXMLTools::getPropertyFromContext(u"PercentageError", + pSeriesStyleContext, pStylesCtxt); + if( aAny.hasValue() && aBarStyle == css::chart::ErrorBarStyle::RELATIVE ) + { + xBarProp->setPropertyValue("PositiveError", aAny); + xBarProp->setPropertyValue("NegativeError", aAny); + } + + switch(aBarStyle) + { + case css::chart::ErrorBarStyle::ERROR_MARGIN: + { + aAny = SchXMLTools::getPropertyFromContext(u"NegativeError", + pSeriesStyleContext,pStylesCtxt); + + xBarProp->setPropertyValue("NegativeError",aAny); + + aAny = SchXMLTools::getPropertyFromContext(u"PositiveError", + pSeriesStyleContext,pStylesCtxt); + + xBarProp->setPropertyValue("PositiveError",aAny); + } + break; + default: + break; + } + +} + +} + +void SchXMLStatisticsObjectContext::startFastElement (sal_Int32 /*Element*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + OUString sAutoStyleName; + OUString aPosRange; + OUString aNegRange; + bool bYError = true; /// Default errorbar, to be backward compatible with older files! + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch (aIter.getToken()) + { + case XML_ELEMENT(CHART, XML_STYLE_NAME): + sAutoStyleName = aIter.toString(); + break; + case XML_ELEMENT(CHART, XML_DIMENSION): + bYError = aIter.toView() == "y"; + break; + case XML_ELEMENT(CHART, XML_ERROR_UPPER_RANGE): + aPosRange = aIter.toString(); + break; + case XML_ELEMENT(CHART, XML_ERROR_LOWER_RANGE): + aNegRange = aIter.toString(); + break; + } + } + + if( sAutoStyleName.isEmpty() ) + return; + + DataRowPointStyle aStyle( DataRowPointStyle::MEAN_VALUE, m_xSeries, -1, 1, sAutoStyleName ); + + switch( meContextType ) + { + case CONTEXT_TYPE_MEAN_VALUE_LINE: + aStyle.meType = DataRowPointStyle::MEAN_VALUE; + break; + case CONTEXT_TYPE_ERROR_INDICATOR: + { + aStyle.meType = DataRowPointStyle::ERROR_INDICATOR; + + uno::Reference< lang::XMultiServiceFactory > xFact = comphelper::getProcessServiceFactory(); + + uno::Reference< beans::XPropertySet > xBarProp( xFact->createInstance("com.sun.star.chart2.ErrorBar" ), + uno::UNO_QUERY ); + + xBarProp->setPropertyValue("ErrorBarStyle",uno::Any(css::chart::ErrorBarStyle::NONE)); + xBarProp->setPropertyValue("PositiveError",uno::Any(0.0)); + xBarProp->setPropertyValue("NegativeError",uno::Any(0.0)); + xBarProp->setPropertyValue("Weight",uno::Any(1.0)); + xBarProp->setPropertyValue("ShowPositiveError",uno::Any(true)); + xBarProp->setPropertyValue("ShowNegativeError",uno::Any(true)); + + // first import defaults from parent style + SetErrorBarStyleProperties( maSeriesStyleName, xBarProp, mrImportHelper ); + SetErrorBarStyleProperties( sAutoStyleName, xBarProp, mrImportHelper ); + SetErrorBarPropertiesFromStyleName( maSeriesStyleName, xBarProp, mrImportHelper, aPosRange, aNegRange ); + SetErrorBarPropertiesFromStyleName( sAutoStyleName, xBarProp, mrImportHelper, aPosRange, aNegRange ); + + uno::Reference< chart2::XChartDocument > xDoc(GetImport().GetModel(),uno::UNO_QUERY); + + if (!aPosRange.isEmpty()) + lcl_setErrorBarSequence(xDoc,xBarProp,aPosRange,true,bYError, mrLSequencesPerIndex); + + if (!aNegRange.isEmpty()) + lcl_setErrorBarSequence(xDoc,xBarProp,aNegRange,false,bYError, mrLSequencesPerIndex); + + if ( !bYError ) + { + aStyle.m_xErrorXProperties.set( xBarProp ); + } + else + { + aStyle.m_xErrorYProperties.set( xBarProp ); + } + } + break; + } + + mrStyleVector.push_back( aStyle ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLPlotAreaContext.hxx b/xmloff/source/chart/SchXMLPlotAreaContext.hxx new file mode 100644 index 000000000..a1de29e18 --- /dev/null +++ b/xmloff/source/chart/SchXMLPlotAreaContext.hxx @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include "SchXMLChartContext.hxx" +#include <rtl/ustrbuf.hxx> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/prstylei.hxx> +#include <xmloff/shapeimport.hxx> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> + +#include "transporttypes.hxx" + +class SvXMLImport; + +namespace com::sun::star { + namespace chart { + class XDiagram; + class X3DDisplay; + class XStatisticDisplay; + } + namespace chart2 { + class XChartDocument; + } + namespace xml::sax { + class XAttributeList; + } +} + +class SchXML3DSceneAttributesHelper : public SdXML3DSceneAttributesHelper +{ +public: + explicit SchXML3DSceneAttributesHelper( SvXMLImport& rImporter ); + virtual ~SchXML3DSceneAttributesHelper(); + + void getCameraDefaultFromDiagram( const css::uno::Reference< css::chart::XDiagram >& xDiagram ); +}; + +class SchXMLPositionAttributesHelper +{ +public: + explicit SchXMLPositionAttributesHelper( SvXMLImport& rImporter ); + + void readPositioningAttribute( sal_Int32 nAttributeToken, std::string_view rValue ); + void readAutomaticPositioningProperties( XMLPropStyleContext const * pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ); + + bool hasPosSize() const; + bool isAutomatic() const; + css::awt::Rectangle getRectangle() const { return css::awt::Rectangle( m_aPosition.X, m_aPosition.Y, m_aSize.Width, m_aSize.Height );} + +private: + SvXMLImport& m_rImport; + + css::awt::Point m_aPosition; + css::awt::Size m_aSize; + + bool m_bHasSizeWidth; + bool m_bHasSizeHeight; + bool m_bHasPositionX; + bool m_bHasPositionY; + bool m_bAutoSize; + bool m_bAutoPosition; +}; + +class SchXMLPlotAreaContext : public SvXMLImportContext +{ +public: + SchXMLPlotAreaContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + const OUString& rXLinkHRefAttributeToIndicateDataProvider, + OUString& rCategoriesAddress, + OUString& rChartAddress, + bool& bHasRangeAtPlotArea, + bool & rAllRangeAddressesAvailable, + bool & rColHasLabels, + bool & rRowHasLabels, + css::chart::ChartDataRowSource & rDataRowSource, + SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles, + const OUString& aChartTypeServiceName, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + const css::awt::Size & rChartSize ); + virtual ~SchXMLPlotAreaContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + +private: + SchXMLImportHelper& mrImportHelper; + css::uno::Reference< css::chart::XDiagram > mxDiagram; + css::uno::Reference< css::chart2::XChartDocument > mxNewDoc; + ::std::vector< SchXMLAxis > maAxes; + OUString& mrCategoriesAddress; + SeriesDefaultsAndStyles& mrSeriesDefaultsAndStyles; + sal_Int32 mnNumOfLinesProp; + bool mbStockHasVolume; + sal_Int32 mnSeries; + GlobalSeriesImportInfo m_aGlobalSeriesImportInfo; + + SchXML3DSceneAttributesHelper maSceneImportHelper; + SchXMLPositionAttributesHelper m_aOuterPositioning;//including axes and axes titles + SchXMLPositionAttributesHelper m_aInnerPositioning;//excluding axes and axes titles + bool mbPercentStacked; + bool m_bAxisPositionAttributeImported; + OUString msAutoStyleName; + const OUString& m_rXLinkHRefAttributeToIndicateDataProvider; + OUString& mrChartAddress; + bool& m_rbHasRangeAtPlotArea; + bool & mrColHasLabels; + bool & mrRowHasLabels; + css::chart::ChartDataRowSource & mrDataRowSource; + OUString maChartTypeServiceName; + + tSchXMLLSequencesPerIndex & mrLSequencesPerIndex; + + bool mbGlobalChartTypeUsedBySeries; + css::awt::Size maChartSize; +}; + +class SchXMLDataLabelSpanContext: public SvXMLImportContext +{ +private: + ::std::vector<OUString>& mrLabels; + OUStringBuffer maCharBuffer; +public: + SchXMLDataLabelSpanContext( SvXMLImport& rImport, ::std::vector<OUString>& rLabels); + virtual void SAL_CALL characters( const OUString& rChars ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +class SchXMLDataLabelParaContext: public SvXMLImportContext +{ +private: + ::std::vector<OUString>& mrLabels; +public: + SchXMLDataLabelParaContext( SvXMLImport& rImport, ::std::vector<OUString>& rLabels); + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +class SchXMLDataLabelContext: public SvXMLImportContext +{ +private: + CustomLabelsInfo& mrLabels; + DataRowPointStyle& mrDataLabelStyle; +public: + SchXMLDataLabelContext(SvXMLImport& rImport, + CustomLabelsInfo& rLabels, DataRowPointStyle& rDataLabel); + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +class SchXMLDataPointContext : public SvXMLImportContext +{ +private: + ::std::vector<DataRowPointStyle>& mrStyleVector; + bool mbHasLabelParagraph = false; + sal_Int32& mrIndex; + DataRowPointStyle mDataPoint; + // We let the data point manage the DataRowPointStyle-struct of its data label + DataRowPointStyle mDataLabel; + +public: + SchXMLDataPointContext( SvXMLImport& rImport, + ::std::vector< DataRowPointStyle >& rStyleVector, + const css::uno::Reference< css::chart2::XDataSeries >& xSeries, + sal_Int32& rIndex, + bool bSymbolSizeForSeriesIsMissingInFile ); + virtual ~SchXMLDataPointContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +class SchXMLCoordinateRegionContext : public SvXMLImportContext +{ +public: + SchXMLCoordinateRegionContext( + SvXMLImport& rImport + , SchXMLPositionAttributesHelper& rPositioning ); + virtual ~SchXMLCoordinateRegionContext() override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + +private: + SchXMLPositionAttributesHelper& m_rPositioning; +}; + +class SchXMLWallFloorContext : public SvXMLImportContext +{ +public: + enum ContextType + { + CONTEXT_TYPE_WALL, + CONTEXT_TYPE_FLOOR + }; + +private: + SchXMLImportHelper& mrImportHelper; + css::uno::Reference< css::chart::X3DDisplay > mxWallFloorSupplier; + ContextType meContextType; + +public: + SchXMLWallFloorContext( SchXMLImportHelper& rImportHelper, + SvXMLImport& rImport, + css::uno::Reference< css::chart::XDiagram > const & xDiagram, + ContextType eContextType ); + virtual ~SchXMLWallFloorContext() override; + virtual void SAL_CALL startFastElement (sal_Int32 Element, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs) override; +}; + +class SchXMLStockContext : public SvXMLImportContext +{ +public: + enum ContextType + { + CONTEXT_TYPE_GAIN, + CONTEXT_TYPE_LOSS, + CONTEXT_TYPE_RANGE + }; + +private: + SchXMLImportHelper& mrImportHelper; + css::uno::Reference< css::chart::XStatisticDisplay > mxStockPropProvider; + ContextType meContextType; + +public: + SchXMLStockContext( SchXMLImportHelper& rImportHelper, + SvXMLImport& rImport, + css::uno::Reference< css::chart::XDiagram > const & xDiagram, + ContextType eContextType ); + virtual ~SchXMLStockContext() override; + virtual void SAL_CALL startFastElement (sal_Int32 Element, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs) override; +}; + +class SchXMLStatisticsObjectContext : public SvXMLImportContext +{ +public: + enum ContextType + { + CONTEXT_TYPE_MEAN_VALUE_LINE, + CONTEXT_TYPE_ERROR_INDICATOR + }; + + SchXMLStatisticsObjectContext( + SchXMLImportHelper& rImportHelper, + SvXMLImport& rImport, + const OUString &rSeriesStyleName, + ::std::vector< DataRowPointStyle >& rStyleVector, + const css::uno::Reference< css::chart2::XDataSeries >& xSeries, + ContextType eContextType, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex ); + + virtual ~SchXMLStatisticsObjectContext() override; + + virtual void SAL_CALL startFastElement (sal_Int32 Element, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs) override; + +private: + SchXMLImportHelper & mrImportHelper; + ::std::vector< DataRowPointStyle > & mrStyleVector; + css::uno::Reference< css::chart2::XDataSeries > m_xSeries; + ContextType meContextType; + OUString maSeriesStyleName; + tSchXMLLSequencesPerIndex& mrLSequencesPerIndex; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLPropertyMappingContext.cxx b/xmloff/source/chart/SchXMLPropertyMappingContext.cxx new file mode 100644 index 000000000..40c3d0452 --- /dev/null +++ b/xmloff/source/chart/SchXMLPropertyMappingContext.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "SchXMLPropertyMappingContext.hxx" +#include "SchXMLTools.hxx" +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include <SchXMLImport.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/chart2/data/XLabeledDataSequence2.hpp> +#include <com/sun/star/chart2/data/XDataSource.hpp> +#include <com/sun/star/chart2/data/XDataSink.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace ::xmloff::token; + +namespace { + +Reference< chart2::data::XLabeledDataSequence2 > createAndAddSequenceToSeries( const OUString& rRole + , const OUString& rRange + , const Reference< chart2::XChartDocument >& xChartDoc + , const Reference< chart2::XDataSeries >& xSeries ) +{ + Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq; + + Reference< chart2::data::XDataSource > xSeriesSource( xSeries,uno::UNO_QUERY ); + + if( !(!rRange.isEmpty() && xChartDoc.is() && xSeriesSource.is()) ) + return xLabeledSeq; + + // create a new sequence + xLabeledSeq = SchXMLTools::GetNewLabeledDataSequence(); + + // set values at the new sequence + Reference< chart2::data::XDataSequence > xSeq = SchXMLTools::CreateDataSequence( rRange, xChartDoc ); + Reference< beans::XPropertySet > xSeqProp( xSeq, uno::UNO_QUERY ); + if( xSeqProp.is()) + xSeqProp->setPropertyValue("Role", uno::Any( rRole)); + xLabeledSeq->setValues( xSeq ); + + Reference< chart2::data::XDataSink > xSink( xSeriesSource, uno::UNO_QUERY ); + if( xSink.is()) + { + Sequence< Reference< chart2::data::XLabeledDataSequence > > aData( xSeriesSource->getDataSequences()); + aData.realloc( aData.getLength() + 1 ); + aData.getArray()[ aData.getLength() - 1 ] = xLabeledSeq; + xSink->setData( aData ); + } + + return xLabeledSeq; +} + +} + +SchXMLPropertyMappingContext::SchXMLPropertyMappingContext( + SvXMLImport& rImport, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + uno::Reference< + chart2::XDataSeries > const & xSeries ): + SvXMLImportContext( rImport ), + mxDataSeries(xSeries), + mrLSequencesPerIndex(rLSequencesPerIndex) +{ + +} + +SchXMLPropertyMappingContext::~SchXMLPropertyMappingContext() +{ +} + +void SchXMLPropertyMappingContext::startFastElement (sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + OUString aRange; + OUString aRole; + // parse attributes + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + OUString aValue = aIter.toString(); + switch( aIter.getToken() ) + { + case XML_ELEMENT(LO_EXT, XML_PROPERTY): + aRole = aValue; + break; + case XML_ELEMENT(LO_EXT, XML_CELL_RANGE_ADDRESS): + aRange = aValue; + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + if( !aRange.isEmpty() && !aRole.isEmpty() ) + { + Reference< chart2::XChartDocument > xChartDoc( GetImport().GetModel(), uno::UNO_QUERY ); + Reference< chart2::data::XLabeledDataSequence2 > xSeq = + createAndAddSequenceToSeries(aRole, aRange, xChartDoc, mxDataSeries); + mrLSequencesPerIndex.emplace( + tSchXMLIndexWithPart( 0, SCH_XML_PART_VALUES), + xSeq); + } +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLPropertyMappingContext.hxx b/xmloff/source/chart/SchXMLPropertyMappingContext.hxx new file mode 100644 index 000000000..1696a32f3 --- /dev/null +++ b/xmloff/source/chart/SchXMLPropertyMappingContext.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "transporttypes.hxx" +#include <xmloff/xmlictxt.hxx> +#include <xmloff/SchXMLImportHelper.hxx> + +namespace com::sun::star { + namespace chart2 { + class XChartDocument; + class XDataSeries; + } +} + +class SchXMLPropertyMappingContext : public SvXMLImportContext +{ +public: + + SchXMLPropertyMappingContext( + SvXMLImport& rImport, + tSchXMLLSequencesPerIndex& rLSequencesPerIndex, + css::uno::Reference< + css::chart2::XDataSeries > const & xSeries ); + + virtual ~SchXMLPropertyMappingContext() override; + + virtual void SAL_CALL startFastElement (sal_Int32 Element, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs) override; +private: + + css::uno::Reference< css::chart2::XDataSeries > mxDataSeries; + + tSchXMLLSequencesPerIndex& mrLSequencesPerIndex; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLRegressionCurveObjectContext.cxx b/xmloff/source/chart/SchXMLRegressionCurveObjectContext.cxx new file mode 100644 index 000000000..16bec57fe --- /dev/null +++ b/xmloff/source/chart/SchXMLRegressionCurveObjectContext.cxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLRegressionCurveObjectContext.hxx" + +#include <SchXMLImport.hxx> + +#include <sax/tools/converter.hxx> +#include <sal/log.hxx> + +#include <xmloff/namespacemap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/prstylei.hxx> +#include <xmloff/xmlstyle.hxx> + +#include <comphelper/processfactory.hxx> + +#include <com/sun/star/awt/Point.hpp> +#include <com/sun/star/chart2/RegressionEquation.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <com/sun/star/xml/sax/XAttributeList.hpp> + +using namespace com::sun::star; +using namespace xmloff::token; + +SchXMLRegressionCurveObjectContext::SchXMLRegressionCurveObjectContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + std::vector< RegressionStyle >& rRegressionStyleVector, + const css::uno::Reference< + css::chart2::XDataSeries >& xSeries, + const awt::Size & rChartSize) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mxSeries( xSeries ), + maChartSize( rChartSize ), + mrRegressionStyleVector( rRegressionStyleVector ) +{ +} + +SchXMLRegressionCurveObjectContext::~SchXMLRegressionCurveObjectContext() +{ +} + +void SchXMLRegressionCurveObjectContext::startFastElement (sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + OUString sAutoStyleName; + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if (aIter.getToken() == XML_ELEMENT(CHART, XML_STYLE_NAME) ) + sAutoStyleName = aIter.toString(); + else + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + + RegressionStyle aStyle( mxSeries, sAutoStyleName ); + mrRegressionStyleVector.push_back( aStyle ); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLRegressionCurveObjectContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + if( nElement == XML_ELEMENT(CHART, XML_EQUATION) ) + { + return new SchXMLEquationContext( + mrImportHelper, GetImport(), maChartSize, mrRegressionStyleVector.back()); + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return nullptr; +} + +SchXMLEquationContext::SchXMLEquationContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + const awt::Size& rChartSize, + RegressionStyle& rRegressionStyle ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mrRegressionStyle( rRegressionStyle ), + maChartSize( rChartSize ) +{} + +SchXMLEquationContext::~SchXMLEquationContext() +{} + +void SchXMLEquationContext::startFastElement (sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + // parse attributes + SchXMLImport& rImport = static_cast< SchXMLImport& >(GetImport()); + OUString sAutoStyleName; + bool bShowEquation = true; + bool bShowRSquare = false; + awt::Point aPosition; + bool bHasXPos = false; + bool bHasYPos = false; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(SVG, XML_X): + case XML_ELEMENT(SVG_COMPAT, XML_X): + rImport.GetMM100UnitConverter().convertMeasureToCore( + aPosition.X, aIter.toView() ); + bHasXPos = true; + break; + case XML_ELEMENT(SVG, XML_Y): + case XML_ELEMENT(SVG_COMPAT, XML_Y): + rImport.GetMM100UnitConverter().convertMeasureToCore( + aPosition.Y, aIter.toView() ); + bHasYPos = true; + break; + case XML_ELEMENT(CHART, XML_DISPLAY_EQUATION): + (void)::sax::Converter::convertBool(bShowEquation, aIter.toView()); + break; + case XML_ELEMENT(CHART, XML_DISPLAY_R_SQUARE): + (void)::sax::Converter::convertBool(bShowRSquare, aIter.toView()); + break; + case XML_ELEMENT(CHART, XML_STYLE_NAME): + sAutoStyleName = aIter.toString(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + if( sAutoStyleName.isEmpty() && !bShowEquation && !bShowRSquare ) + return; + + uno::Reference< beans::XPropertySet > xEquationProperties = chart2::RegressionEquation::create( comphelper::getProcessComponentContext() ); + + if( !sAutoStyleName.isEmpty() ) + { + const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext(); + if( pStylesCtxt ) + { + const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), sAutoStyleName ); + + XMLPropStyleContext* pPropStyleContext = + const_cast< XMLPropStyleContext* >( dynamic_cast< const XMLPropStyleContext* >( pStyle )); + + if( pPropStyleContext ) + pPropStyleContext->FillPropertySet( xEquationProperties ); + } + } + xEquationProperties->setPropertyValue( "ShowEquation", uno::Any( bShowEquation )); + xEquationProperties->setPropertyValue( "ShowCorrelationCoefficient", uno::Any( bShowRSquare )); + + if( bHasXPos && bHasYPos ) + { + chart2::RelativePosition aRelPos; + aRelPos.Primary = static_cast< double >( aPosition.X ) / static_cast< double >( maChartSize.Width ); + aRelPos.Secondary = static_cast< double >( aPosition.Y ) / static_cast< double >( maChartSize.Height ); + xEquationProperties->setPropertyValue( "RelativePosition", uno::Any( aRelPos )); + } + mrRegressionStyle.m_xEquationProperties.set( xEquationProperties ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLRegressionCurveObjectContext.hxx b/xmloff/source/chart/SchXMLRegressionCurveObjectContext.hxx new file mode 100644 index 000000000..13b796cd2 --- /dev/null +++ b/xmloff/source/chart/SchXMLRegressionCurveObjectContext.hxx @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <com/sun/star/awt/Size.hpp> +#include <xmloff/xmlictxt.hxx> +#include <xmloff/SchXMLImportHelper.hxx> + +#include "transporttypes.hxx" + +class SchXMLRegressionCurveObjectContext : public SvXMLImportContext +{ +public: + SchXMLRegressionCurveObjectContext( + SchXMLImportHelper& rImportHelper, + SvXMLImport& rImport, + std::vector< RegressionStyle >& rRegressionStyleVector, + const css::uno::Reference< css::chart2::XDataSeries >& xSeries, + const css::awt::Size & rChartSize ); + + virtual ~SchXMLRegressionCurveObjectContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + +private: + + SchXMLImportHelper& mrImportHelper; + css::uno::Reference<css::chart2::XDataSeries > mxSeries; + css::awt::Size maChartSize; + std::vector< RegressionStyle >& mrRegressionStyleVector; +}; + +class SchXMLEquationContext : public SvXMLImportContext +{ +public: + SchXMLEquationContext( + SchXMLImportHelper& rImportHelper, + SvXMLImport& rImport, + const css::awt::Size & rChartSize, + RegressionStyle & rRegressionStyle ); + + virtual ~SchXMLEquationContext() override; + + virtual void SAL_CALL startFastElement (sal_Int32 Element, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& Attribs) override; + +private: + SchXMLImportHelper& mrImportHelper; + RegressionStyle& mrRegressionStyle; + css::awt::Size maChartSize; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLSeries2Context.cxx b/xmloff/source/chart/SchXMLSeries2Context.cxx new file mode 100644 index 000000000..56b983048 --- /dev/null +++ b/xmloff/source/chart/SchXMLSeries2Context.cxx @@ -0,0 +1,1307 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLSeries2Context.hxx" +#include "SchXMLPlotAreaContext.hxx" +#include "SchXMLRegressionCurveObjectContext.hxx" +#include "SchXMLPropertyMappingContext.hxx" +#include "SchXMLTools.hxx" + +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XRegressionCurve.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> +#include <com/sun/star/chart2/data/XDataSink.hpp> +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> + +#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp> +#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp> +#include <com/sun/star/chart2/DataPointCustomLabelField.hpp> + +#include <com/sun/star/chart/ChartAxisAssign.hpp> +#include <com/sun/star/chart/ChartSymbolType.hpp> +#include <com/sun/star/chart/ChartDataCaption.hpp> +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart/XChartDocument.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/chart/ChartLegendPosition.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/XVisualObject.hpp> + +#include <comphelper/processfactory.hxx> + +#include <sal/log.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/SchXMLSeriesHelper.hxx> +#include <SchXMLImport.hxx> +#include <xmloff/prstylei.hxx> +#include <tools/diagnose_ex.h> + +#include <algorithm> // std::find_if + +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ + +class SchXMLDomain2Context : public SvXMLImportContext +{ +private: + ::std::vector< OUString > & mrAddresses; + +public: + SchXMLDomain2Context( SvXMLImport& rImport, + ::std::vector< OUString > & rAddresses ); + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +SchXMLDomain2Context::SchXMLDomain2Context( + SvXMLImport& rImport, + ::std::vector< OUString > & rAddresses ) : + SvXMLImportContext( rImport ), + mrAddresses( rAddresses ) +{ +} + +void SchXMLDomain2Context::startFastElement( + sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + if (aIter.getToken() == XML_ELEMENT(TABLE, XML_CELL_RANGE_ADDRESS) ) + mrAddresses.push_back( aIter.toString() ); + else + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } +} + +void lcl_setAutomaticSymbolSize( const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp, const SvXMLImport& rImport ) +{ + awt::Size aSymbolSize(140,140);//old default for standard sized charts 7cm height + + uno::Reference< chart::XChartDocument > xChartDoc( rImport.GetModel(), uno::UNO_QUERY ); + if( xChartDoc.is() ) + { + double fScale = 1; + uno::Reference< beans::XPropertySet > xLegendProp( xChartDoc->getLegend(), uno::UNO_QUERY ); + chart::ChartLegendPosition aLegendPosition = chart::ChartLegendPosition_NONE; + if( xLegendProp.is() && (xLegendProp->getPropertyValue("Alignment") >>= aLegendPosition) + && chart::ChartLegendPosition_NONE != aLegendPosition ) + { + + double fFontHeight = 6.0; + if( xLegendProp->getPropertyValue("CharHeight") >>= fFontHeight ) + fScale = 0.75*fFontHeight/6.0; + } + else + { + uno::Reference< embed::XVisualObject > xVisualObject( rImport.GetModel(), uno::UNO_QUERY ); + if( xVisualObject.is() ) + { + awt::Size aPageSize( xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ) ); + fScale = aPageSize.Height/7000.0; + } + } + if( fScale>0 ) + { + aSymbolSize.Height = static_cast<sal_Int32>( fScale * aSymbolSize.Height ); + aSymbolSize.Width = aSymbolSize.Height; + } + } + xSeriesOrPointProp->setPropertyValue("SymbolSize",uno::Any( aSymbolSize )); +} + +void lcl_setSymbolSizeIfNeeded( const uno::Reference< beans::XPropertySet >& xSeriesOrPointProp, const SvXMLImport& rImport ) +{ + if( !xSeriesOrPointProp.is() ) + return; + + sal_Int32 nSymbolType = chart::ChartSymbolType::NONE; + if( !(xSeriesOrPointProp.is() && ( xSeriesOrPointProp->getPropertyValue("SymbolType") >>= nSymbolType)) ) + return; + + if(chart::ChartSymbolType::NONE!=nSymbolType) + { + if( chart::ChartSymbolType::BITMAPURL==nSymbolType ) + { + //set special size for graphics to indicate to use the bitmap size itself + xSeriesOrPointProp->setPropertyValue("SymbolSize",uno::Any( awt::Size(-1,-1) )); + } + else + { + lcl_setAutomaticSymbolSize( xSeriesOrPointProp, rImport ); + } + } +} + +void lcl_resetSymbolSizeForPointsIfNecessary( const uno::Reference< beans::XPropertySet >& xPointProp, const SvXMLImport& rImport + , const XMLPropStyleContext * pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ) +{ + uno::Any aASymbolSize( SchXMLTools::getPropertyFromContext( u"SymbolSize", pPropStyleContext, pStylesCtxt ) ); + if( !aASymbolSize.hasValue() ) + lcl_setSymbolSizeIfNeeded( xPointProp, rImport ); +} + +void lcl_setLinkNumberFormatToSourceIfNeeded( const uno::Reference< beans::XPropertySet >& xPointProp + , const XMLPropStyleContext* pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ) +{ + uno::Any aAny( SchXMLTools::getPropertyFromContext(u"LinkNumberFormatToSource", pPropStyleContext, pStylesCtxt) ); + if( aAny.hasValue() ) + return; + + if( !xPointProp.is() ) + return; + + bool bLinkToSource = false; + if( xPointProp.is() && (xPointProp->getPropertyValue("LinkNumberFormatToSource") >>= bLinkToSource) ) + { + if( bLinkToSource ) + { + xPointProp->setPropertyValue("LinkNumberFormatToSource", uno::Any(false)); + } + } +} + +void lcl_insertErrorBarLSequencesToMap( + tSchXMLLSequencesPerIndex & rInOutMap, + const uno::Reference< beans::XPropertySet > & xSeriesProp ) +{ + Reference< chart2::data::XDataSource > xErrorBarSource; + if( ( xSeriesProp->getPropertyValue( "ErrorBarY" ) >>= xErrorBarSource ) && + xErrorBarSource.is() ) + { + const Sequence< Reference< chart2::data::XLabeledDataSequence > > aLSequences( + xErrorBarSource->getDataSequences()); + for( const auto& rLSequence : aLSequences ) + { + // use "0" as data index. This is ok, as it is not used for error bars + rInOutMap.emplace( + tSchXMLIndexWithPart( 0, SCH_XML_PART_ERROR_BARS ), rLSequence ); + } + } +} + +Reference< chart2::data::XLabeledDataSequence2 > lcl_createAndAddSequenceToSeries( const OUString& rRole + , const OUString& rRange + , const Reference< chart2::XChartDocument >& xChartDoc + , const Reference< chart2::XDataSeries >& xSeries ) +{ + Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq; + + Reference< chart2::data::XDataSource > xSeriesSource( xSeries,uno::UNO_QUERY ); + Reference< chart2::data::XDataSink > xSeriesSink( xSeries, uno::UNO_QUERY ); + + if( !(!rRange.isEmpty() && xChartDoc.is() && xSeriesSource.is() && xSeriesSink.is()) ) + return xLabeledSeq; + + // create a new sequence + xLabeledSeq = SchXMLTools::GetNewLabeledDataSequence(); + + // set values at the new sequence + Reference< chart2::data::XDataSequence > xSeq = SchXMLTools::CreateDataSequence( rRange, xChartDoc ); + Reference< beans::XPropertySet > xSeqProp( xSeq, uno::UNO_QUERY ); + if( xSeqProp.is()) + xSeqProp->setPropertyValue("Role", uno::Any( rRole)); + xLabeledSeq->setValues( xSeq ); + + // add new sequence to data series / push to front to have the correct sequence order if charttype is changed afterwards + const Sequence< Reference< chart2::data::XLabeledDataSequence > > aOldSeq( xSeriesSource->getDataSequences()); + sal_Int32 nOldCount = aOldSeq.getLength(); + Sequence< Reference< chart2::data::XLabeledDataSequence > > aNewSeq( nOldCount + 1 ); + auto pNewSeq = aNewSeq.getArray(); + pNewSeq[0].set(xLabeledSeq, uno::UNO_QUERY_THROW); + std::copy(aOldSeq.begin(), aOldSeq.end(), std::next(pNewSeq)); + xSeriesSink->setData( aNewSeq ); + + return xLabeledSeq; +} + +XMLPropStyleContext* lcl_GetStylePropContext( + const SvXMLStylesContext* pStylesCtxt, + const SvXMLStyleContext*& rpStyle, + OUString const & rStyleName ) +{ + rpStyle = pStylesCtxt->FindStyleChildContext( SchXMLImportHelper::GetChartFamilyID(), rStyleName ); + XMLPropStyleContext* pPropStyleContext = + const_cast< XMLPropStyleContext* >(dynamic_cast< const XMLPropStyleContext* >( rpStyle )); + return pPropStyleContext; +} + +} // anonymous namespace + +SchXMLSeries2Context::SchXMLSeries2Context( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + const Reference< chart2::XChartDocument > & xNewDoc, + std::vector< SchXMLAxis >& rAxes, + ::std::vector< DataRowPointStyle >& rStyleVector, + ::std::vector< RegressionStyle >& rRegressionStyleVector, + sal_Int32 nSeriesIndex, + bool bStockHasVolume, + GlobalSeriesImportInfo& rGlobalSeriesImportInfo, + const OUString & aGlobalChartTypeName, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + bool& rGlobalChartTypeUsedBySeries, + const awt::Size & rChartSize ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ), + mxNewDoc( xNewDoc ), + mrAxes( rAxes ), + mrStyleVector( rStyleVector ), + mrRegressionStyleVector( rRegressionStyleVector ), + mnSeriesIndex( nSeriesIndex ), + mnDataPointIndex( 0 ), + m_bStockHasVolume( bStockHasVolume ), + m_rGlobalSeriesImportInfo(rGlobalSeriesImportInfo), + mpAttachedAxis( nullptr ), + mnAttachedAxis( 0 ), + maGlobalChartTypeName( aGlobalChartTypeName ), + maSeriesChartTypeName( aGlobalChartTypeName ), + m_bHasDomainContext(false), + mrLSequencesPerIndex( rLSequencesPerIndex ), + mrGlobalChartTypeUsedBySeries( rGlobalChartTypeUsedBySeries ), + mbSymbolSizeIsMissingInFile(false), + maChartSize( rChartSize ), + // A series manages the DataRowPointStyle-struct of a data-label child element. + mDataLabel(DataRowPointStyle::DATA_LABEL_SERIES, OUString{}) +{ + if( aGlobalChartTypeName == "com.sun.star.chart2.DonutChartType" ) + { + maSeriesChartTypeName = "com.sun.star.chart2.PieChartType"; + maGlobalChartTypeName = maSeriesChartTypeName; + } +} + +SchXMLSeries2Context::~SchXMLSeries2Context() +{ + SAL_WARN_IF( !maPostponedSequences.empty(), "xmloff.chart", "maPostponedSequences is NULL"); +} + +void SchXMLSeries2Context::startFastElement (sal_Int32 /*Element*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + // parse attributes + mnAttachedAxis = 1; + + bool bHasRange = false; + OUString aSeriesLabelRange; + OUString aSeriesLabelString; + bool bHideLegend = false; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + OUString aValue = aIter.toString(); + switch(aIter.getToken()) + { + case XML_ELEMENT(CHART, XML_VALUES_CELL_RANGE_ADDRESS): + m_aSeriesRange = aValue; + bHasRange = true; + break; + case XML_ELEMENT(CHART, XML_LABEL_CELL_ADDRESS): + aSeriesLabelRange = aValue; + break; + case XML_ELEMENT(LO_EXT, XML_LABEL_STRING): + aSeriesLabelString = aValue; + break; + case XML_ELEMENT(CHART, XML_ATTACHED_AXIS): + { + sal_Int32 nNumOfAxes = mrAxes.size(); + for( sal_Int32 nCurrent = 0; nCurrent < nNumOfAxes; nCurrent++ ) + { + if( aValue == mrAxes[ nCurrent ].aName && + mrAxes[ nCurrent ].eDimension == SCH_XML_AXIS_Y ) + { + mpAttachedAxis = &( mrAxes[ nCurrent ] ); + } + } + } + break; + case XML_ELEMENT(CHART, XML_STYLE_NAME): + msAutoStyleName = aValue; + break; + case XML_ELEMENT(CHART, XML_CLASS): + { + OUString aClassName; + sal_uInt16 nClassPrefix = + GetImport().GetNamespaceMap().GetKeyByAttrValueQName( + aValue, &aClassName ); + if( XML_NAMESPACE_CHART == nClassPrefix ) + maSeriesChartTypeName = SchXMLTools::GetChartTypeByClassName( aClassName, false /* bUseOldNames */ ); + + if( maSeriesChartTypeName.isEmpty()) + maSeriesChartTypeName = aClassName; + } + break; + case XML_ELEMENT(LO_EXT, XML_HIDE_LEGEND): + bHideLegend = aValue.toBoolean(); + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + if( mpAttachedAxis ) + { + if( mpAttachedAxis->nAxisIndex > 0 ) + { + // secondary axis => property has to be set (primary is default) + mnAttachedAxis = 2; + } + } + + try + { + SAL_WARN_IF( !mxNewDoc.is(), "xmloff.chart", "mxNewDoc is NULL"); + if( m_rGlobalSeriesImportInfo.rbAllRangeAddressesAvailable && ! bHasRange ) + m_rGlobalSeriesImportInfo.rbAllRangeAddressesAvailable = false; + + bool bIsCandleStick = maGlobalChartTypeName == "com.sun.star.chart2.CandleStickChartType"; + if( !maSeriesChartTypeName.isEmpty() ) + { + bIsCandleStick = maSeriesChartTypeName == "com.sun.star.chart2.CandleStickChartType"; + } + else + { + if( bIsCandleStick + && m_bStockHasVolume + && mnSeriesIndex == 0 ) + { + maSeriesChartTypeName = "com.sun.star.chart2.ColumnChartType"; + bIsCandleStick = false; + } + else + { + maSeriesChartTypeName = maGlobalChartTypeName; + } + } + if( ! mrGlobalChartTypeUsedBySeries ) + mrGlobalChartTypeUsedBySeries = (maSeriesChartTypeName == maGlobalChartTypeName); + sal_Int32 const nCoordinateSystemIndex = 0;//so far we can only import one coordinate system + m_xSeries.set( + SchXMLImportHelper::GetNewDataSeries( mxNewDoc, nCoordinateSystemIndex, maSeriesChartTypeName, ! mrGlobalChartTypeUsedBySeries )); + Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( SchXMLTools::GetNewLabeledDataSequence(), uno::UNO_QUERY_THROW ); + + Reference< beans::XPropertySet > xSeriesProp( m_xSeries, uno::UNO_QUERY ); + if (xSeriesProp.is()) + { + if (bHideLegend) + xSeriesProp->setPropertyValue("ShowLegendEntry", uno::Any(false)); + + if( bIsCandleStick ) + { + // set default color for range-line to black (before applying styles) + xSeriesProp->setPropertyValue("Color", + uno::Any( sal_Int32( 0x000000 ))); // black + } + else if ( maSeriesChartTypeName == "com.sun.star.chart2.PieChartType" ) + { + //@todo: this property should be saved + xSeriesProp->setPropertyValue("VaryColorsByPoint", + uno::Any( true )); + } + + } + + Reference<chart2::data::XDataProvider> xDataProvider(mxNewDoc->getDataProvider()); + Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xDataProvider, uno::UNO_QUERY); + + Reference<chart2::data::XDataSequence> xSequenceValues; + + // values + if (xPivotTableDataProvider.is()) // is pivot chart + { + xSequenceValues.set(xPivotTableDataProvider->createDataSequenceOfValuesByIndex(mnSeriesIndex)); + } + else + { + if (bHasRange && !m_aSeriesRange.isEmpty()) + xSequenceValues = SchXMLTools::CreateDataSequence(m_aSeriesRange, mxNewDoc); + } + + Reference<beans::XPropertySet> xSeqProp(xSequenceValues, uno::UNO_QUERY); + if (xSeqProp.is()) + { + OUString aMainRole("values-y"); + if (maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType") + aMainRole = "values-size"; + xSeqProp->setPropertyValue("Role", uno::Any(aMainRole)); + } + xLabeledSeq->setValues(xSequenceValues); + + // register for setting local data if external data provider is not present + maPostponedSequences.emplace( + tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_VALUES ), xLabeledSeq ); + + // label + Reference<chart2::data::XDataSequence> xSequenceLabel; + + if (xPivotTableDataProvider.is()) + { + xSequenceLabel.set(xPivotTableDataProvider->createDataSequenceOfLabelsByIndex(mnSeriesIndex)); + } + else + { + if (!aSeriesLabelRange.isEmpty()) + { + xSequenceLabel.set(SchXMLTools::CreateDataSequence(aSeriesLabelRange, mxNewDoc)); + } + else if (!aSeriesLabelString.isEmpty()) + { + xSequenceLabel.set(SchXMLTools::CreateDataSequenceWithoutConvert(aSeriesLabelString, mxNewDoc)); + } + } + + //Labels should always include hidden cells + Reference<beans::XPropertySet> xSeqLabelProp(xSequenceLabel, uno::UNO_QUERY); + if (xSeqLabelProp.is() && xSeqLabelProp->getPropertySetInfo()->hasPropertyByName("IncludeHiddenCells")) + { + xSeqLabelProp->setPropertyValue( "IncludeHiddenCells", uno::Any(true)); + } + + xLabeledSeq->setLabel(xSequenceLabel); + + // Note: Even if we have no label, we have to register the label + // for creation, because internal data always has labels. If + // they don't exist in the original, auto-generated labels are + // used for the internal data. + maPostponedSequences.emplace( + tSchXMLIndexWithPart( m_rGlobalSeriesImportInfo.nCurrentDataIndex, SCH_XML_PART_LABEL ), xLabeledSeq ); + + Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( &xLabeledSeq, 1 ); + Reference< chart2::data::XDataSink > xSink( m_xSeries, uno::UNO_QUERY_THROW ); + xSink->setData( aSeq ); + } + catch( const uno::Exception &) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + + //init mbSymbolSizeIsMissingInFile: + try + { + if( !msAutoStyleName.isEmpty() ) + { + const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext(); + if( pStylesCtxt ) + { + const SvXMLStyleContext* pStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), msAutoStyleName ); + + const XMLPropStyleContext* pPropStyleContext = dynamic_cast< const XMLPropStyleContext * >( pStyle ); + + uno::Any aASymbolSize( SchXMLTools::getPropertyFromContext( u"SymbolSize" + , pPropStyleContext, pStylesCtxt ) ); + mbSymbolSizeIsMissingInFile = !aASymbolSize.hasValue(); + } + } + } + catch( const uno::Exception & ) + { + } +} + +namespace { + +struct DomainInfo +{ + DomainInfo( const OUString& rRole, const OUString& rRange, sal_Int32 nIndex ) + : aRole(rRole), aRange(rRange), nIndexForLocalData(nIndex) + {} + + OUString aRole; + OUString aRange; + sal_Int32 nIndexForLocalData; +}; + +} + +void SchXMLSeries2Context::endFastElement(sal_Int32 ) +{ + // special handling for different chart types. This is necessary as the + // roles are not yet saved in the file format + sal_Int32 nDomainCount = maDomainAddresses.size(); + bool bIsScatterChart = maSeriesChartTypeName == "com.sun.star.chart2.ScatterChartType"; + bool bIsBubbleChart = maSeriesChartTypeName == "com.sun.star.chart2.BubbleChartType"; + bool bDeleteSeries = false; + std::vector< DomainInfo > aDomainInfos; + + //different handling for different chart types necessary + if( bIsScatterChart || ( nDomainCount==1 && !bIsBubbleChart ) ) + { + DomainInfo aDomainInfo( "values-x", m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ; + bool bCreateXValues = true; + if( !maDomainAddresses.empty() ) + { + if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() ) + { + m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front(); + m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + } + aDomainInfo.aRange = maDomainAddresses.front(); + aDomainInfo.nIndexForLocalData = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + else if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() && !m_bHasDomainContext && mnSeriesIndex==0 ) + { + if( SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( GetImport().GetModel() ) ) //wrong old chart files: + { + //for xy charts the first series needs to have a domain + //if this by error iss not the case the first series is taken s x values + //needed for wrong files created while having an addin (e.g. BoxPlot) + m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = m_aSeriesRange; + m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + bDeleteSeries = true; + bCreateXValues = false;//they will be created for the next series + } + } + if( bCreateXValues ) + aDomainInfos.push_back( aDomainInfo ); + } + else if( bIsBubbleChart ) + { + if( nDomainCount>1 ) + { + DomainInfo aDomainInfo( "values-x", maDomainAddresses[1], m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ; + if( m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() ) + { + //for bubble chart the second domain contains the x values which should become an index smaller than y values for own data table + //->so second first + m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress = maDomainAddresses[1]; + m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + } + aDomainInfos.push_back( aDomainInfo ); + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + else if( !m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress.isEmpty() ) + { + DomainInfo aDomainInfo( "values-x", m_rGlobalSeriesImportInfo.aFirstSecondDomainAddress, m_rGlobalSeriesImportInfo.nFirstSecondDomainIndex ) ; + aDomainInfos.push_back( aDomainInfo ); + } + if( nDomainCount>0) + { + DomainInfo aDomainInfo( "values-y", maDomainAddresses.front(), m_rGlobalSeriesImportInfo.nCurrentDataIndex ) ; + if( m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() ) + { + m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress = maDomainAddresses.front(); + m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex = m_rGlobalSeriesImportInfo.nCurrentDataIndex; + } + aDomainInfos.push_back( aDomainInfo ); + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + else if( !m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress.isEmpty() ) + { + DomainInfo aDomainInfo( "values-y", m_rGlobalSeriesImportInfo.aFirstFirstDomainAddress, m_rGlobalSeriesImportInfo.nFirstFirstDomainIndex ) ; + aDomainInfos.push_back( aDomainInfo ); + } + } + + if( bDeleteSeries ) + { + //delete created series + SchXMLImportHelper::DeleteDataSeries( + m_xSeries, Reference< chart2::XChartDocument >( GetImport().GetModel(), uno::UNO_QUERY ) ); + } + else + { + //add style + if( !msAutoStyleName.isEmpty() || mnAttachedAxis != 1 ) + { + DataRowPointStyle aStyle( + DataRowPointStyle::DATA_SERIES, + m_xSeries, + -1, 1, + msAutoStyleName, mnAttachedAxis ); + aStyle.mbSymbolSizeForSeriesIsMissingInFile=mbSymbolSizeIsMissingInFile; + mrStyleVector.push_back( aStyle ); + } + // And styles for a data-label child element too. In contrast to data-labels as child of data points, + // an information about absolute position is useless here. We need only style information. + if (!mDataLabel.msStyleName.isEmpty()) + { + mDataLabel.msStyleNameOfParent = msAutoStyleName; + mDataLabel.m_xSeries = m_xSeries; + mDataLabel.mnAttachedAxis = mnAttachedAxis; // not needed, but be consistent with its parent + mrStyleVector.push_back(mDataLabel); + } + } + + for( std::vector< DomainInfo >::reverse_iterator aIt( aDomainInfos.rbegin() ); aIt!= aDomainInfos.rend(); ++aIt ) + { + DomainInfo aDomainInfo( *aIt ); + Reference< chart2::data::XLabeledDataSequence2 > xLabeledSeq = + lcl_createAndAddSequenceToSeries( aDomainInfo.aRole, aDomainInfo.aRange, mxNewDoc, m_xSeries ); + if( xLabeledSeq.is() ) + { + // register for setting local data if external data provider is not present + mrLSequencesPerIndex.emplace( + tSchXMLIndexWithPart( aDomainInfo.nIndexForLocalData, SCH_XML_PART_VALUES ), + Reference< chart2::data::XLabeledDataSequence >(xLabeledSeq, uno::UNO_QUERY_THROW) ); + } + } + + if( !bDeleteSeries ) + { + for (auto const& postponedSequence : maPostponedSequences) + { + sal_Int32 nNewIndex = postponedSequence.first.first + nDomainCount; + mrLSequencesPerIndex.emplace( tSchXMLIndexWithPart( nNewIndex, postponedSequence.first.second ), postponedSequence.second ); + } + m_rGlobalSeriesImportInfo.nCurrentDataIndex++; + } + maPostponedSequences.clear(); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLSeries2Context::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + + switch(nElement) + { + case XML_ELEMENT(CHART, XML_DOMAIN): + if( m_xSeries.is()) + { + m_bHasDomainContext = true; + pContext = new SchXMLDomain2Context( + GetImport(), maDomainAddresses ); + } + break; + + case XML_ELEMENT(CHART, XML_MEAN_VALUE): + pContext = new SchXMLStatisticsObjectContext( + mrImportHelper, GetImport(), + msAutoStyleName, + mrStyleVector, m_xSeries, + SchXMLStatisticsObjectContext::CONTEXT_TYPE_MEAN_VALUE_LINE, + mrLSequencesPerIndex ); + break; + case XML_ELEMENT(CHART, XML_REGRESSION_CURVE): + pContext = new SchXMLRegressionCurveObjectContext( + mrImportHelper, GetImport(), + mrRegressionStyleVector, + m_xSeries, maChartSize ); + break; + case XML_ELEMENT(CHART, XML_ERROR_INDICATOR): + pContext = new SchXMLStatisticsObjectContext( + mrImportHelper, GetImport(), + msAutoStyleName, + mrStyleVector, m_xSeries, + SchXMLStatisticsObjectContext::CONTEXT_TYPE_ERROR_INDICATOR, + mrLSequencesPerIndex ); + break; + + case XML_ELEMENT(CHART, XML_DATA_POINT): + pContext = new SchXMLDataPointContext( GetImport(), + mrStyleVector, m_xSeries, mnDataPointIndex, mbSymbolSizeIsMissingInFile ); + break; + case XML_ELEMENT(CHART, XML_DATA_LABEL): + // CustomLabels are useless for a data label element as child of a series, because it serves as default + // for all data labels. But the ctor expects it, so use that of the mDataLabel struct as ersatz. + pContext = new SchXMLDataLabelContext(GetImport(), mDataLabel.mCustomLabels, + mDataLabel); + break; + + case XML_ELEMENT(LO_EXT, XML_PROPERTY_MAPPING): + pContext = new SchXMLPropertyMappingContext( + GetImport(), + mrLSequencesPerIndex, m_xSeries ); + break; + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + } + + return pContext; +} + +//static +void SchXMLSeries2Context::initSeriesPropertySets( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const uno::Reference< frame::XModel >& xChartModel ) +{ + // iterate over series first and remind propertysets in map + // new api <-> old api wrapper + ::std::map< Reference< chart2::XDataSeries >, Reference< beans::XPropertySet > > aSeriesMap; + for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES ) + continue; + + if( !seriesStyle.m_xOldAPISeries.is() ) + seriesStyle.m_xOldAPISeries = SchXMLSeriesHelper::createOldAPISeriesPropertySet( seriesStyle.m_xSeries, xChartModel ); + + aSeriesMap[seriesStyle.m_xSeries] = seriesStyle.m_xOldAPISeries; + + } + + //initialize m_xOldAPISeries for all other styles also + for (auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType == DataRowPointStyle::DATA_SERIES ) + continue; + seriesStyle.m_xOldAPISeries = aSeriesMap[seriesStyle.m_xSeries]; + } +} + +//static +void SchXMLSeries2Context::setDefaultsToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles ) +{ + // iterate over series + // call initSeriesPropertySets first + + for (const auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES ) + continue; + + try + { + uno::Reference< beans::XPropertySet > xSeries( seriesStyle.m_xOldAPISeries ); + if( !xSeries.is() ) + continue; + + if( rSeriesDefaultsAndStyles.maSymbolTypeDefault.hasValue() ) + xSeries->setPropertyValue("SymbolType",rSeriesDefaultsAndStyles.maSymbolTypeDefault); + if( rSeriesDefaultsAndStyles.maDataCaptionDefault.hasValue() ) + xSeries->setPropertyValue("DataCaption",rSeriesDefaultsAndStyles.maDataCaptionDefault); + + if( rSeriesDefaultsAndStyles.maErrorIndicatorDefault.hasValue() ) + xSeries->setPropertyValue("ErrorIndicator",rSeriesDefaultsAndStyles.maErrorIndicatorDefault); + if( rSeriesDefaultsAndStyles.maErrorCategoryDefault.hasValue() ) + xSeries->setPropertyValue("ErrorCategory",rSeriesDefaultsAndStyles.maErrorCategoryDefault); + if( rSeriesDefaultsAndStyles.maConstantErrorLowDefault.hasValue() ) + xSeries->setPropertyValue("ConstantErrorLow",rSeriesDefaultsAndStyles.maConstantErrorLowDefault); + if( rSeriesDefaultsAndStyles.maConstantErrorHighDefault.hasValue() ) + xSeries->setPropertyValue("ConstantErrorHigh",rSeriesDefaultsAndStyles.maConstantErrorHighDefault); + if( rSeriesDefaultsAndStyles.maPercentageErrorDefault.hasValue() ) + xSeries->setPropertyValue("PercentageError",rSeriesDefaultsAndStyles.maPercentageErrorDefault); + if( rSeriesDefaultsAndStyles.maErrorMarginDefault.hasValue() ) + xSeries->setPropertyValue("ErrorMargin",rSeriesDefaultsAndStyles.maErrorMarginDefault); + + if( rSeriesDefaultsAndStyles.maMeanValueDefault.hasValue() ) + xSeries->setPropertyValue("MeanValue",rSeriesDefaultsAndStyles.maMeanValueDefault); + if( rSeriesDefaultsAndStyles.maRegressionCurvesDefault.hasValue() ) + xSeries->setPropertyValue("RegressionCurves",rSeriesDefaultsAndStyles.maRegressionCurvesDefault); + } + catch( uno::Exception & ) + { + //end of series reached + } + } +} + +// ODF has the line and fill properties in a <style:style> element, which is referenced by the +// <chart:data-label> element. But LibreOffice has them as special label properties of the series +// or point respectively. The following array maps the API name of the ODF property to the name of +// the internal property. Those are of kind "LabelFoo". +// The array is used in methods setStylesToSeries and setStylesToDataPoints. +const std::pair<OUString, OUString> aApiToLabelFooPairs[] + = { { "LineStyle", "LabelBorderStyle" }, + { "LineWidth", "LabelBorderWidth" }, + { "LineColor", "LabelBorderColor" }, + // The name "LabelBorderDash" is defined, but the associated API name "LineDash" belongs to + // the <draw:stroke-dash> element and is not used directly as line property. + //{"LineDash", "LabelBorderDash"}, + { "LineDashName", "LabelBorderDashName" }, + { "LineTransparence", "LabelBorderTransparency" }, + { "FillStyle", "LabelFillStyle" }, + { "FillBackground", "LabelFillBackground" }, + { "FillHatchName", "LabelFillHatchName" }, + { "FillColor", "LabelFillColor" } }; + + +//static +void SchXMLSeries2Context::setStylesToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName + , const SchXMLImportHelper& rImportHelper + , const SvXMLImport& rImport + , bool bIsStockChart + , tSchXMLLSequencesPerIndex & rInOutLSequencesPerIndex ) +{ + // iterate over series + for (const auto & seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if (seriesStyle.meType != DataRowPointStyle::DATA_SERIES) + continue; + try + { + uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries ); + if( !xSeriesProp.is() ) + continue; + + if( seriesStyle.mnAttachedAxis != 1 ) + { + xSeriesProp->setPropertyValue("Axis" + , uno::Any(chart::ChartAxisAssign::SECONDARY_Y) ); + } + + if( seriesStyle.msStyleName.isEmpty()) + continue; + + if( rCurrStyleName != seriesStyle.msStyleName ) + { + rCurrStyleName = seriesStyle.msStyleName; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + //set style to series + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + + if (!pPropStyleContext) + continue; + + // error bar style must be set before the other error + // bar properties (which may be alphabetically before + // this property) + bool bHasErrorBarRangesFromData = false; + { + static const OUStringLiteral aErrorBarStylePropName( u"ErrorBarStyle"); + uno::Any aErrorBarStyle( + SchXMLTools::getPropertyFromContext( aErrorBarStylePropName, pPropStyleContext, pStylesCtxt )); + if( aErrorBarStyle.hasValue()) + { + xSeriesProp->setPropertyValue( aErrorBarStylePropName, aErrorBarStyle ); + sal_Int32 eEBStyle = chart::ErrorBarStyle::NONE; + bHasErrorBarRangesFromData = + ( ( aErrorBarStyle >>= eEBStyle ) && + eEBStyle == chart::ErrorBarStyle::FROM_DATA ); + } + } + + //don't set the style to the min max line series of a stock chart + //otherwise the min max line properties gets overwritten and the series becomes invisible typically + if (bIsStockChart) + { + if (SchXMLSeriesHelper::isCandleStickSeries( + seriesStyle.m_xSeries, + rImportHelper.GetChartDocument())) + continue; + } + + // Has the series a data-label child element? + auto pItLabel + = std::find_if(rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(), + rSeriesDefaultsAndStyles.maSeriesStyleVector.end(), + [&seriesStyle](const DataRowPointStyle& rStyle) { + return rStyle.meType == DataRowPointStyle::DATA_LABEL_SERIES + && rStyle.msStyleNameOfParent == seriesStyle.msStyleName; + }); + if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end()) + { + // Bring the information from the data-label to the series + const SvXMLStyleContext* pLabelStyleContext(pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName)); + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext* pLabelPropStyleContext = const_cast<XMLPropStyleContext*>( + dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext)); + if (pLabelPropStyleContext) + { + // Test each to be mapped property whether the data-label has a value for it. + // If found, set it at series. + uno::Reference<beans::XPropertySetInfo> xSeriesPropInfo( + xSeriesProp->getPropertySetInfo()); + for (const auto& rPropPair : aApiToLabelFooPairs) + { + uno::Any aPropValue(SchXMLTools::getPropertyFromContext( + rPropPair.first, pLabelPropStyleContext, pStylesCtxt)); + if (aPropValue.hasValue() + && xSeriesPropInfo->hasPropertyByName(rPropPair.second)) + xSeriesProp->setPropertyValue(rPropPair.second, aPropValue); + } + } + } + + pPropStyleContext->FillPropertySet( xSeriesProp ); + if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile ) + lcl_setSymbolSizeIfNeeded( xSeriesProp, rImport ); + if( bHasErrorBarRangesFromData ) + lcl_insertErrorBarLSequencesToMap( rInOutLSequencesPerIndex, xSeriesProp ); + + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" ); + } + } +} + +// static +void SchXMLSeries2Context::setStylesToRegressionCurves( + SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles, + const SvXMLStylesContext* pStylesCtxt, + const SvXMLStyleContext*& rpStyle, + OUString const & rCurrentStyleName ) +{ + // iterate over regression etc + for (auto const& regressionStyle : rSeriesDefaultsAndStyles.maRegressionStyleVector) + { + try + { + OUString aServiceName; + XMLPropStyleContext* pPropStyleContext = nullptr; + + if (!rCurrentStyleName.isEmpty()) + { + XMLPropStyleContext* pCurrent = lcl_GetStylePropContext(pStylesCtxt, rpStyle, rCurrentStyleName); + if( pCurrent ) + { + pPropStyleContext = pCurrent; + uno::Any aAny = SchXMLTools::getPropertyFromContext(u"RegressionType", pPropStyleContext, pStylesCtxt); + if ( aAny.hasValue() ) + { + aAny >>= aServiceName; + } + } + } + + if (!regressionStyle.msStyleName.isEmpty()) + { + XMLPropStyleContext* pCurrent = lcl_GetStylePropContext(pStylesCtxt, rpStyle, regressionStyle.msStyleName); + if( pCurrent ) + { + pPropStyleContext = pCurrent; + uno::Any aAny = SchXMLTools::getPropertyFromContext(u"RegressionType", pPropStyleContext, pStylesCtxt); + if ( aAny.hasValue() ) + { + aAny >>= aServiceName; + } + } + } + + if( !aServiceName.isEmpty() ) + { + Reference< lang::XMultiServiceFactory > xMSF = comphelper::getProcessServiceFactory(); + Reference< chart2::XRegressionCurve > xRegCurve( xMSF->createInstance( aServiceName ), uno::UNO_QUERY_THROW ); + Reference< chart2::XRegressionCurveContainer > xRegCurveCont( regressionStyle.m_xSeries, uno::UNO_QUERY_THROW ); + + Reference< beans::XPropertySet > xCurveProperties( xRegCurve, uno::UNO_QUERY ); + if( pPropStyleContext != nullptr) + pPropStyleContext->FillPropertySet( xCurveProperties ); + + xRegCurve->setEquationProperties( regressionStyle.m_xEquationProperties ); + + xRegCurveCont->addRegressionCurve( xRegCurve ); + } + } + catch( const uno::Exception& ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" ); + } + + } +} + +// static +void SchXMLSeries2Context::setStylesToStatisticsObjects( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName ) +{ + // iterate over regression etc + for (auto const& seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType == DataRowPointStyle::ERROR_INDICATOR || + seriesStyle.meType == DataRowPointStyle::MEAN_VALUE ) + { + if ( seriesStyle.meType == DataRowPointStyle::ERROR_INDICATOR ) + { + uno::Reference< beans::XPropertySet > xNewSeriesProp(seriesStyle.m_xSeries,uno::UNO_QUERY); + + if (seriesStyle.m_xErrorXProperties.is()) + xNewSeriesProp->setPropertyValue("ErrorBarX",uno::Any(seriesStyle.m_xErrorXProperties)); + + if (seriesStyle.m_xErrorYProperties.is()) + xNewSeriesProp->setPropertyValue("ErrorBarY",uno::Any(seriesStyle.m_xErrorYProperties)); + } + + try + { + uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries ); + if( !xSeriesProp.is() ) + continue; + + if( !seriesStyle.msStyleName.isEmpty()) + { + if( rCurrStyleName != seriesStyle.msStyleName ) + { + rCurrStyleName = seriesStyle.msStyleName; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + if( pPropStyleContext ) + { + Reference< beans::XPropertySet > xStatPropSet; + switch( seriesStyle.meType ) + { + case DataRowPointStyle::MEAN_VALUE: + xSeriesProp->getPropertyValue("DataMeanValueProperties") >>= xStatPropSet; + break; + case DataRowPointStyle::ERROR_INDICATOR: + xSeriesProp->getPropertyValue("DataErrorProperties") >>= xStatPropSet; + break; + default: + break; + } + if( xStatPropSet.is()) + pPropStyleContext->FillPropertySet( xStatPropSet ); + } + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to series" ); + } + } + } +} + +//static +void SchXMLSeries2Context::setStylesToDataPoints( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName + , const SchXMLImportHelper& rImportHelper + , const SvXMLImport& rImport + , bool bIsStockChart, bool bIsDonutChart, bool bSwitchOffLinesForScatter ) +{ + for (auto const& seriesStyle : rSeriesDefaultsAndStyles.maSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_POINT ) + continue; + + if( seriesStyle.m_nPointIndex == -1 ) + continue; + + uno::Reference< beans::XPropertySet > xSeriesProp( seriesStyle.m_xOldAPISeries ); + if(!xSeriesProp.is()) + continue; + + //ignore datapoint properties for stock charts + //... todo ... + if( bIsStockChart ) + { + if( SchXMLSeriesHelper::isCandleStickSeries( seriesStyle.m_xSeries, rImportHelper.GetChartDocument() ) ) + continue; + } + + // data point style + for( sal_Int32 i = 0; i < seriesStyle.m_nPointRepeat; i++ ) + { + try + { + uno::Reference< beans::XPropertySet > xPointProp( + SchXMLSeriesHelper::createOldAPIDataPointPropertySet( seriesStyle.m_xSeries, seriesStyle.m_nPointIndex + i + , rImportHelper.GetChartDocument() ) ); + + if( !xPointProp.is() ) + continue; + + if( bIsDonutChart ) + { + //set special series styles for donut charts first + if( rCurrStyleName != seriesStyle.msSeriesStyleNameForDonuts ) + { + rCurrStyleName = seriesStyle.msSeriesStyleNameForDonuts; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + if( pPropStyleContext ) + pPropStyleContext->FillPropertySet( xPointProp ); + } + + try + { + //need to set this explicitly here for old files as the new api does not support this property fully anymore + if( bSwitchOffLinesForScatter ) + xPointProp->setPropertyValue("Lines",uno::Any(false)); + } + catch( const uno::Exception & ) + { + } + + if( rCurrStyleName != seriesStyle.msStyleName ) + { + rCurrStyleName = seriesStyle.msStyleName; + rpStyle = pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), rCurrStyleName ); + } + + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext * pPropStyleContext = + const_cast< XMLPropStyleContext * >( + dynamic_cast< const XMLPropStyleContext * >( rpStyle )); + if (pPropStyleContext) + { + // Has the point a data-label child element? + auto pItLabel = std::find_if( + rSeriesDefaultsAndStyles.maSeriesStyleVector.begin(), + rSeriesDefaultsAndStyles.maSeriesStyleVector.end(), + [&seriesStyle](const DataRowPointStyle& rStyle) { + return rStyle.meType == DataRowPointStyle::DATA_LABEL_POINT + && rStyle.msStyleNameOfParent == seriesStyle.msStyleName; + }); + if (pItLabel != rSeriesDefaultsAndStyles.maSeriesStyleVector.end()) + { + // Bring the information from the data-label to the point + const SvXMLStyleContext* pLabelStyleContext( + pStylesCtxt->FindStyleChildContext( + SchXMLImportHelper::GetChartFamilyID(), (*pItLabel).msStyleName)); + // note: SvXMLStyleContext::FillPropertySet is not const + XMLPropStyleContext* pLabelPropStyleContext + = const_cast<XMLPropStyleContext*>( + dynamic_cast<const XMLPropStyleContext*>(pLabelStyleContext)); + if (pLabelPropStyleContext) + { + // Test each to be mapped property whether the data-label has a value for it. + // If found, set it at the point. + uno::Reference<beans::XPropertySetInfo> xPointPropInfo( + xPointProp->getPropertySetInfo()); + for (const auto& rPropPair : aApiToLabelFooPairs) + { + uno::Any aPropValue(SchXMLTools::getPropertyFromContext( + rPropPair.first, pLabelPropStyleContext, pStylesCtxt)); + if (aPropValue.hasValue() + && xPointPropInfo->hasPropertyByName(rPropPair.second)) + xPointProp->setPropertyValue(rPropPair.second, aPropValue); + } + } + } + + pPropStyleContext->FillPropertySet( xPointProp ); + if( seriesStyle.mbSymbolSizeForSeriesIsMissingInFile ) + lcl_resetSymbolSizeForPointsIfNecessary( xPointProp, rImport, pPropStyleContext, pStylesCtxt ); + if( !pPropStyleContext->isEmptyDataStyleName() ) + lcl_setLinkNumberFormatToSourceIfNeeded( xPointProp, pPropStyleContext, pStylesCtxt ); + } + + // Custom labels might be passed as property + if(const size_t nLabelCount = seriesStyle.mCustomLabels.mLabels.size(); nLabelCount > 0) + { + auto& rCustomLabels = seriesStyle.mCustomLabels; + + Sequence< Reference<chart2::XDataPointCustomLabelField>> xLabels(nLabelCount); + auto pxLabels = xLabels.getArray(); + Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() ); + for( size_t j = 0; j < nLabelCount; ++j ) + { + Reference< chart2::XDataPointCustomLabelField > xCustomLabel = chart2::DataPointCustomLabelField::create(xContext); + pxLabels[j] = xCustomLabel; + xCustomLabel->setString(rCustomLabels.mLabels[j]); + if ( j == 0 && rCustomLabels.mbDataLabelsRange) + { + xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE); + xCustomLabel->setGuid(rCustomLabels.msLabelGuid); + xCustomLabel->setCellRange(rCustomLabels.msLabelsCellRange); + xCustomLabel->setDataLabelsRange(true); + } + else + { + xCustomLabel->setFieldType(chart2::DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT); + } + + // Restore character properties on the text span manually, till + // SchXMLExportHelper_Impl::exportCustomLabel() does not write the style. + uno::Reference<beans::XPropertySetInfo> xPointPropInfo + = xPointProp->getPropertySetInfo(); + if (xPointPropInfo.is()) + { + uno::Sequence<beans::Property> aProperties = xPointPropInfo->getProperties(); + for (const auto& rProperty : std::as_const(aProperties)) + { + if (!rProperty.Name.startsWith("Char") + || rProperty.Name.startsWith("Chart")) + { + continue; + } + + xCustomLabel->setPropertyValue( + rProperty.Name, xPointProp->getPropertyValue(rProperty.Name)); + } + } + } + + xPointProp->setPropertyValue("CustomLabelFields", uno::Any(xLabels)); + xPointProp->setPropertyValue("DataCaption", uno::Any(chart::ChartDataCaption::CUSTOM)); + } + + if( seriesStyle.mCustomLabelPos[0] != 0.0 || seriesStyle.mCustomLabelPos[1] != 0.0 ) + { + chart2::RelativePosition aCustomlabelPosition; + aCustomlabelPosition.Primary = seriesStyle.mCustomLabelPos[0]; + aCustomlabelPosition.Secondary = seriesStyle.mCustomLabelPos[1]; + xPointProp->setPropertyValue("CustomLabelPosition", uno::Any(aCustomlabelPosition)); + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during setting styles to data points" ); + } + } + } // styles iterator +} + +//static +void SchXMLSeries2Context::switchSeriesLinesOff( ::std::vector< DataRowPointStyle >& rSeriesStyleVector ) +{ + // iterate over series + for (auto const& seriesStyle : rSeriesStyleVector) + { + if( seriesStyle.meType != DataRowPointStyle::DATA_SERIES ) + continue; + + try + { + uno::Reference< beans::XPropertySet > xSeries( seriesStyle.m_xOldAPISeries ); + if( !xSeries.is() ) + continue; + + xSeries->setPropertyValue("Lines",uno::Any(false)); + } + catch( uno::Exception & ) + { + //end of series reached + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLSeries2Context.hxx b/xmloff/source/chart/SchXMLSeries2Context.hxx new file mode 100644 index 000000000..4996ecf8b --- /dev/null +++ b/xmloff/source/chart/SchXMLSeries2Context.hxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include "transporttypes.hxx" +#include "SchXMLChartContext.hxx" +#include <xmloff/xmlictxt.hxx> +#include <xmloff/SchXMLImportHelper.hxx> +#include <xmloff/xmlstyle.hxx> + +#include <vector> + +namespace com::sun::star { + namespace chart2 { + class XChartDocument; + class XDataSeries; + } + namespace awt { + struct Size; + } +} + +// class for child contexts: series, data point and statistics objects +class SchXMLSeries2Context : public SvXMLImportContext +{ +private: + SchXMLImportHelper& mrImportHelper; + css::uno::Reference< css::chart2::XChartDocument > mxNewDoc; + ::std::vector< SchXMLAxis >& mrAxes; + ::std::vector< DataRowPointStyle >& mrStyleVector; + ::std::vector< RegressionStyle >& mrRegressionStyleVector; + css::uno::Reference< css::chart2::XDataSeries > m_xSeries; + sal_Int32 mnSeriesIndex; + sal_Int32 mnDataPointIndex; + bool m_bStockHasVolume; + + GlobalSeriesImportInfo& m_rGlobalSeriesImportInfo; + + SchXMLAxis* mpAttachedAxis; + sal_Int32 mnAttachedAxis; + OUString msAutoStyleName; + ::std::vector< OUString > maDomainAddresses; + OUString maGlobalChartTypeName; + OUString maSeriesChartTypeName; + OUString m_aSeriesRange; + bool m_bHasDomainContext; + tSchXMLLSequencesPerIndex & mrLSequencesPerIndex; + tSchXMLLSequencesPerIndex maPostponedSequences; + bool& mrGlobalChartTypeUsedBySeries; + bool mbSymbolSizeIsMissingInFile; + css::awt::Size maChartSize; + // We let the series manage the DataRowPointStyle-struct of its data label + DataRowPointStyle mDataLabel; + +public: + SchXMLSeries2Context( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + const css::uno::Reference< css::chart2::XChartDocument > & xNewDoc, + std::vector< SchXMLAxis >& rAxes, + ::std::vector< DataRowPointStyle >& rStyleVector, + ::std::vector< RegressionStyle >& rRegressionStyleVector, + sal_Int32 nSeriesIndex, + bool bStockHasVolume, + GlobalSeriesImportInfo& rGlobalSeriesImportInfo, + const OUString & aGlobalChartTypeName, + tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + bool& rGlobalChartTypeUsedBySeries, + const css::awt::Size & rChartSize ); + virtual ~SchXMLSeries2Context() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + static void initSeriesPropertySets( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const css::uno::Reference< css::frame::XModel >& xChartModel ); + + static void setDefaultsToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles ); + + static void setStylesToSeries( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName + , const SchXMLImportHelper& rImportHelper + , const SvXMLImport& rImport + , bool bIsStockChart + , tSchXMLLSequencesPerIndex & rInOutLSequencesPerIndex ); + + static void setStylesToStatisticsObjects( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString &rCurrStyleName ); + + static void setStylesToRegressionCurves( + SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles, + const SvXMLStylesContext* pStylesCtxt, + const SvXMLStyleContext*& rpStyle, + OUString const &rCurrStyleName ); + + static void setStylesToDataPoints( SeriesDefaultsAndStyles& rSeriesDefaultsAndStyles + , const SvXMLStylesContext* pStylesCtxt + , const SvXMLStyleContext*& rpStyle + , OUString& rCurrStyleName + , const SchXMLImportHelper& rImportHelper + , const SvXMLImport& rImport + , bool bIsStockChart, bool bIsDonutChart, bool bSwitchOffLinesForScatter ); + + static void switchSeriesLinesOff( ::std::vector< DataRowPointStyle >& rSeriesStyleVector ); +}; + +// INCLUDED_XMLOFF_SOURCE_CHART_SCHXMLSERIES2CONTEXT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLSeriesHelper.cxx b/xmloff/source/chart/SchXMLSeriesHelper.cxx new file mode 100644 index 000000000..31a197596 --- /dev/null +++ b/xmloff/source/chart/SchXMLSeriesHelper.cxx @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <xmloff/SchXMLSeriesHelper.hxx> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <sal/log.hxx> +#include <tools/diagnose_ex.h> + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +::std::vector< Reference< chart2::XDataSeries > > + SchXMLSeriesHelper::getDataSeriesFromDiagram( + const Reference< chart2::XDiagram > & xDiagram ) +{ + ::std::vector< Reference< chart2::XDataSeries > > aResult; + + try + { + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( + xDiagram, uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( + xCooSysCnt->getCoordinateSystems()); + for( const auto& rCooSys : aCooSysSeq ) + { + Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XChartType > > aChartTypeSeq( xCTCnt->getChartTypes()); + for( const auto& rChartType : aChartTypeSeq ) + { + Reference< chart2::XDataSeriesContainer > xDSCnt( rChartType, uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries() ); + aResult.insert( aResult.end(), aSeriesSeq.begin(), aSeriesSeq.end() ); + } + } + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + + return aResult; +} + +::std::map< Reference< chart2::XDataSeries >, sal_Int32 > SchXMLSeriesHelper::getDataSeriesIndexMapFromDiagram( + const Reference< chart2::XDiagram > & xDiagram ) +{ + ::std::map< Reference< chart2::XDataSeries >, sal_Int32 > aRet; + + sal_Int32 nIndex=0; + + ::std::vector< Reference< chart2::XDataSeries > > aSeriesVector( SchXMLSeriesHelper::getDataSeriesFromDiagram( xDiagram )); + for( const Reference< chart2::XDataSeries >& xSeries : aSeriesVector ) + { + if( xSeries.is() ) + { + if( aRet.end() == aRet.find(xSeries) ) + aRet[xSeries]=nIndex; + } + nIndex++; + } + return aRet; +} + +namespace { +uno::Reference< chart2::XChartType > lcl_getChartTypeOfSeries( + const uno::Reference< chart2::XDiagram >& xDiagram + , const Reference< chart2::XDataSeries >& xSeries ) +{ + if(!xDiagram.is()) + return nullptr; + + //iterate through the model to find the given xSeries + //the found parent indicates the charttype + + //iterate through all coordinate systems + uno::Reference< chart2::XCoordinateSystemContainer > xCooSysContainer( xDiagram, uno::UNO_QUERY ); + if( !xCooSysContainer.is()) + return nullptr; + + const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > aCooSysList( xCooSysContainer->getCoordinateSystems() ); + for( const auto& xCooSys : aCooSysList ) + { + //iterate through all chart types in the current coordinate system + uno::Reference< chart2::XChartTypeContainer > xChartTypeContainer( xCooSys, uno::UNO_QUERY ); + SAL_WARN_IF( !xChartTypeContainer.is(), "xmloff.chart", "xChartTypeContainer is NULL"); + if( !xChartTypeContainer.is() ) + continue; + const uno::Sequence< uno::Reference< chart2::XChartType > > aChartTypeList( xChartTypeContainer->getChartTypes() ); + for( const auto& xChartType : aChartTypeList ) + { + //iterate through all series in this chart type + uno::Reference< chart2::XDataSeriesContainer > xDataSeriesContainer( xChartType, uno::UNO_QUERY ); + SAL_WARN_IF( !xDataSeriesContainer.is(), "xmloff.chart", "xDataSeriesContainer is NULL"); + if( !xDataSeriesContainer.is() ) + continue; + + const uno::Sequence< uno::Reference< chart2::XDataSeries > > aSeriesList( xDataSeriesContainer->getDataSeries() ); + if (std::find(aSeriesList.begin(), aSeriesList.end(), xSeries) != aSeriesList.end()) + return xChartType; + } + } + return nullptr; +} +} + +bool SchXMLSeriesHelper::isCandleStickSeries( + const Reference< chart2::XDataSeries >& xSeries + , const Reference< frame::XModel >& xChartModel ) +{ + bool bRet = false; + + uno::Reference< chart2::XChartDocument > xNewDoc( xChartModel, uno::UNO_QUERY ); + if( xNewDoc.is() ) + { + uno::Reference< chart2::XDiagram > xNewDiagram( xNewDoc->getFirstDiagram() ); + if( xNewDiagram.is() ) + { + uno::Reference< chart2::XChartType > xChartType( lcl_getChartTypeOfSeries( + xNewDiagram, xSeries ) ); + if( xChartType.is() ) + { + OUString aServiceName( xChartType->getChartType() ); + if( aServiceName == "com.sun.star.chart2.CandleStickChartType" ) + bRet = true; + } + } + } + return bRet; +} + +//static +uno::Reference< beans::XPropertySet > SchXMLSeriesHelper::createOldAPISeriesPropertySet( + const uno::Reference< chart2::XDataSeries >& xSeries + , const uno::Reference< frame::XModel >& xChartModel ) +{ + uno::Reference< beans::XPropertySet > xRet; + + if( xSeries.is() ) + { + try + { + uno::Reference< lang::XMultiServiceFactory > xFactory( xChartModel, uno::UNO_QUERY ); + if( xFactory.is() ) + { + xRet.set( xFactory->createInstance( "com.sun.star.comp.chart2.DataSeriesWrapper" ), uno::UNO_QUERY ); + Reference< lang::XInitialization > xInit( xRet, uno::UNO_QUERY ); + if(xInit.is()) + { + xInit->initialize( { uno::Any(xSeries) }); + } + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught SchXMLSeriesHelper::createOldAPISeriesPropertySet" ); + } + } + + return xRet; +} + +//static +uno::Reference< beans::XPropertySet > SchXMLSeriesHelper::createOldAPIDataPointPropertySet( + const uno::Reference< chart2::XDataSeries >& xSeries + , sal_Int32 nPointIndex + , const uno::Reference< frame::XModel >& xChartModel ) +{ + uno::Reference< beans::XPropertySet > xRet; + + if( xSeries.is() ) + { + try + { + uno::Reference< lang::XMultiServiceFactory > xFactory( xChartModel, uno::UNO_QUERY ); + if( xFactory.is() ) + { + xRet.set( xFactory->createInstance( "com.sun.star.comp.chart2.DataSeriesWrapper" ), uno::UNO_QUERY ); + Reference< lang::XInitialization > xInit( xRet, uno::UNO_QUERY ); + if(xInit.is()) + { + xInit->initialize({ uno::Any(xSeries), uno::Any(nPointIndex) }); + } + } + } + catch( const uno::Exception & ) + { + TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught SchXMLSeriesHelper::createOldAPIDataPointPropertySet" ); + } + } + + return xRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLTableContext.cxx b/xmloff/source/chart/SchXMLTableContext.cxx new file mode 100644 index 000000000..27ae73113 --- /dev/null +++ b/xmloff/source/chart/SchXMLTableContext.cxx @@ -0,0 +1,1048 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sax/tools/converter.hxx> + +#include "SchXMLTableContext.hxx" +#include "SchXMLParagraphContext.hxx" +#include "SchXMLTextListContext.hxx" +#include <SchXMLImport.hxx> +#include "SchXMLTools.hxx" +#include "transporttypes.hxx" +#include <XMLStringBufferImportContext.hxx> +#include <o3tl/safeint.hxx> +#include <o3tl/string_view.hxx> +#include <sal/log.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/namespacemap.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/string.hxx> +#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XInternalDataProvider.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> + +#include <limits> +#include <vector> +#include <algorithm> +#include <iterator> +#include <string_view> + +using namespace com::sun::star; +using namespace ::xmloff::token; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; + +namespace +{ + +constexpr OUStringLiteral aCategoriesRange = u"categories"; + +typedef ::std::multimap< OUString, OUString > + lcl_tOriginalRangeToInternalRangeMap; + +struct lcl_ApplyCellToData +{ + explicit lcl_ApplyCellToData( Sequence< double > & rOutData ) : + m_rData( rOutData ), + m_nIndex( 0 ), + m_nSize( rOutData.getLength()) + { + } + + void operator() ( const SchXMLCell & rCell ) + { + if( m_nIndex < m_nSize ) + { + auto pData = m_rData.getArray(); + if( rCell.eType == SCH_CELL_TYPE_FLOAT ) + pData[m_nIndex] = rCell.fValue; + else + pData[m_nIndex] = std::numeric_limits<double>::quiet_NaN(); + } + ++m_nIndex; + } + + sal_Int32 getCurrentIndex() const + { + return m_nIndex; + } + +private: + Sequence< double > & m_rData; + sal_Int32 m_nIndex; + sal_Int32 m_nSize; +}; + +void lcl_fillRangeMapping( + const SchXMLTable & rTable, + lcl_tOriginalRangeToInternalRangeMap & rOutRangeMap, + chart::ChartDataRowSource eDataRowSource ) +{ + sal_Int32 nRowOffset = ( rTable.bHasHeaderRow ? 1 : 0 ); + sal_Int32 nColOffset = ( rTable.bHasHeaderColumn ? 1 : 0 ); + + const OUString lcl_aCategoriesRange(aCategoriesRange); + static const OUStringLiteral lcl_aLabelPrefix(u"label "); + + // Fill range mapping + const size_t nTableRowCount( rTable.aData.size()); + for( size_t nRow = 0; nRow < nTableRowCount; ++nRow ) + { + const ::std::vector< SchXMLCell > & rRow( rTable.aData[nRow] ); + const size_t nTableColCount( rRow.size()); + for( size_t nCol = 0; nCol < nTableColCount; ++nCol ) + { + const OUString aRangeId( rRow[nCol].aRangeId ); + if( !aRangeId.isEmpty()) + { + if( eDataRowSource == chart::ChartDataRowSource_COLUMNS ) + { + if( nCol == 0 && rTable.bHasHeaderColumn ) + { + SAL_WARN_IF( static_cast< sal_Int32 >( nRow ) != nRowOffset, "xmloff.chart", "nRow != nRowOffset" ); + rOutRangeMap.emplace(aRangeId, lcl_aCategoriesRange); + } + else + { + OUString aColNumStr = OUString::number( nCol - nColOffset); + if( nRow == 0 && rTable.bHasHeaderRow ) + rOutRangeMap.emplace( aRangeId, lcl_aLabelPrefix + aColNumStr ); + else + rOutRangeMap.emplace( aRangeId, aColNumStr ); + } + } + else // eDataRowSource == chart::ChartDataRowSource_ROWS + { + if( nRow == 0 && rTable.bHasHeaderRow ) + { + SAL_WARN_IF( static_cast< sal_Int32 >( nCol ) != nColOffset, "xmloff.chart", "nCol != nColOffset" ); + rOutRangeMap.emplace( aRangeId, lcl_aCategoriesRange ); + } + else + { + OUString aRowNumStr = OUString::number( nRow - nRowOffset); + if( nCol == 0 && rTable.bHasHeaderColumn ) + rOutRangeMap.emplace( aRangeId, lcl_aLabelPrefix + aRowNumStr ); + else + rOutRangeMap.emplace( aRangeId, aRowNumStr ); + } + } + } + } + } +} + +Reference< chart2::data::XDataSequence > + lcl_reassignDataSequence( + const Reference< chart2::data::XDataSequence > & xSequence, + const Reference< chart2::data::XDataProvider > & xDataProvider, + lcl_tOriginalRangeToInternalRangeMap & rRangeMap, + const OUString & rRange ) +{ + Reference< chart2::data::XDataSequence > xResult( xSequence ); + lcl_tOriginalRangeToInternalRangeMap::iterator aIt( rRangeMap.find( rRange )); + if( aIt != rRangeMap.end()) + { + // set sequence with correct data + xResult.set( xDataProvider->createDataSequenceByRangeRepresentation( aIt->second )); + // remove translation, because it was used + rRangeMap.erase( aIt ); + } + + return xResult; +} + +bool lcl_mapContainsRange( + lcl_tOriginalRangeToInternalRangeMap & rRangeMap, + const OUString & rRange ) +{ + lcl_tOriginalRangeToInternalRangeMap::iterator aIt( rRangeMap.find( rRange )); + return ( aIt != rRangeMap.end()); +} + +bool lcl_tableOfRangeMatches( + std::u16string_view rRange, + std::u16string_view rTableName ) +{ + // both strings are non-empty and the table name is part of the range + return ( !rRange.empty() && + !rTableName.empty() && + (rRange.find( rTableName ) != std::u16string_view::npos )); +} + +} // anonymous namespace + +// class SchXMLTableContext +SchXMLTableContext::SchXMLTableContext( SvXMLImport& rImport, + SchXMLTable& aTable ) : + SvXMLImportContext( rImport ), + mrTable( aTable ), + mbHasRowPermutation( false ), + mbHasColumnPermutation( false ) +{ + mrTable.nColumnIndex = -1; + mrTable.nMaxColumnIndex = -1; + mrTable.nRowIndex = -1; + mrTable.aData.clear(); +} + +SchXMLTableContext::~SchXMLTableContext() +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + + switch(nElement) + { + case XML_ELEMENT(TABLE, XML_TABLE_HEADER_COLUMNS): + mrTable.bHasHeaderColumn = true; + [[fallthrough]]; + case XML_ELEMENT(TABLE, XML_TABLE_COLUMNS): + pContext = new SchXMLTableColumnsContext( GetImport(), mrTable ); + break; + + case XML_ELEMENT(TABLE, XML_TABLE_COLUMN): + pContext = new SchXMLTableColumnContext( GetImport(), mrTable ); + break; + + case XML_ELEMENT(TABLE, XML_TABLE_HEADER_ROWS): + mrTable.bHasHeaderRow = true; + [[fallthrough]]; + case XML_ELEMENT(TABLE, XML_TABLE_ROWS): + pContext = new SchXMLTableRowsContext( GetImport(), mrTable ); + break; + + case XML_ELEMENT(TABLE, XML_TABLE_ROW): + pContext = new SchXMLTableRowContext( GetImport(), mrTable ); + break; + default: + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + } + + return pContext; +} + +void SchXMLTableContext::startFastElement (sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + // get table-name + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_NAME): + mrTable.aTableNameOfFile = aIter.toString(); + break; + case XML_ELEMENT(TABLE, XML_PROTECTED): + if ( IsXMLToken( aIter, XML_TRUE ) ) + { + mrTable.bProtected = true; + } + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } +} + +void SchXMLTableContext::endFastElement(sal_Int32 ) +{ + if( mbHasColumnPermutation ) + { + SAL_WARN_IF( mbHasRowPermutation, "xmloff.chart", "mbHasColumnPermutation is true" ); + const auto & aPermutation( maColumnPermutation ); + SAL_WARN_IF( !aPermutation.hasElements(), "xmloff.chart", "aPermutation is NULL"); + if( !aPermutation.hasElements()) + return; + + // permute the values of all rows according to aPermutation + for( auto& rRow : mrTable.aData ) + { + bool bModified = false; + ::std::vector< SchXMLCell > aModifiedRow; + const size_t nPermSize = aPermutation.size(); + SAL_WARN_IF( static_cast< sal_Int32 >( nPermSize ) - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())), "xmloff.chart", "nPermSize - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())"); + const size_t nRowSize = rRow.size(); + const size_t nDestSize = ::std::min( nPermSize, nRowSize ); + for( size_t nDestinationIndex = 0; nDestinationIndex < nDestSize; ++nDestinationIndex ) + { + const size_t nSourceIndex = static_cast< size_t >( aPermutation[ nDestinationIndex ] ); + if( nSourceIndex != nDestinationIndex && + nSourceIndex < nRowSize ) + { + // copy original on first real permutation + if( !bModified ) + { + SAL_WARN_IF( !aModifiedRow.empty(), "xmloff.chart", "aModifiedRow is NOT NULL"); + aModifiedRow.insert( aModifiedRow.end(), rRow.begin(), rRow.end() ); + SAL_WARN_IF( aModifiedRow.empty(), "xmloff.chart", "aModifiedRow is NULL"); + } + SAL_WARN_IF( nDestinationIndex >= aModifiedRow.size(), "xmloff.chart", "nDestinationIndex >= aModifiedRow.size()"); + aModifiedRow[ nDestinationIndex ] = rRow[ nSourceIndex ]; + bModified = true; + } + } + // copy back + if( bModified ) + ::std::copy( aModifiedRow.begin(), aModifiedRow.end(), rRow.begin()); + } + } + else if( mbHasRowPermutation ) + { + const auto & aPermutation( maRowPermutation ); + SAL_WARN_IF( !aPermutation.hasElements(), "xmloff.chart", "aPermutation is NULL"); + if( !aPermutation.hasElements()) + return; + + bool bModified = false; + const size_t nPermSize = aPermutation.size(); + SAL_WARN_IF( static_cast< sal_Int32 >( nPermSize ) - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())), "xmloff.chart", "nPermSize - 1 != *(::std::max_element( aPermutation.begin(), aPermutation.end())"); + const size_t nTableRowCount = mrTable.aData.size(); + const size_t nDestSize = ::std::min( nPermSize, nTableRowCount ); + ::std::vector< ::std::vector< SchXMLCell > > aDestination; + for( size_t nDestinationIndex = 0; nDestinationIndex < nDestSize; ++nDestinationIndex ) + { + const size_t nSourceIndex = static_cast< size_t >( aPermutation[ nDestinationIndex ] ); + if( nSourceIndex != nDestinationIndex && + nSourceIndex < nTableRowCount ) + { + // copy original on first real permutation + if( !bModified ) + { + SAL_WARN_IF( !aDestination.empty(), "xmloff.chart", "aDestination is NOT NULL"); + aDestination.insert( aDestination.end(), mrTable.aData.begin(), mrTable.aData.end()); + SAL_WARN_IF( aDestination.empty(), "xmloff.chart", "aDestination is NULL"); + } + SAL_WARN_IF( nDestinationIndex >= aDestination.size(), "xmloff.chart", "nDestinationIndex >= aDestination.size()"); + aDestination[ nDestinationIndex ] = mrTable.aData[ nSourceIndex ]; + bModified = true; + } + } + if( bModified ) + { + // copy back + ::std::copy( aDestination.begin(), aDestination.end(), mrTable.aData.begin()); + } + } +} + +void SchXMLTableContext::setRowPermutation( const uno::Sequence< sal_Int32 > & rPermutation ) +{ + maRowPermutation = rPermutation; + mbHasRowPermutation = rPermutation.hasElements(); + + if( mbHasRowPermutation && mbHasColumnPermutation ) + { + mbHasColumnPermutation = false; + maColumnPermutation.realloc( 0 ); + } +} + +void SchXMLTableContext::setColumnPermutation( const uno::Sequence< sal_Int32 > & rPermutation ) +{ + maColumnPermutation = rPermutation; + mbHasColumnPermutation = rPermutation.hasElements(); + + if( mbHasColumnPermutation && mbHasRowPermutation ) + { + mbHasRowPermutation = false; + maRowPermutation.realloc( 0 ); + } +} + +// classes for columns +// class SchXMLTableColumnsContext +SchXMLTableColumnsContext::SchXMLTableColumnsContext( + SvXMLImport& rImport, + SchXMLTable& aTable ) : + SvXMLImportContext( rImport ), + mrTable( aTable ) +{ +} + +SchXMLTableColumnsContext::~SchXMLTableColumnsContext() +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableColumnsContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + + if( nElement == XML_ELEMENT(TABLE, XML_TABLE_COLUMN) ) + pContext = new SchXMLTableColumnContext( GetImport(), mrTable ); + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + + return pContext; +} + +// class SchXMLTableColumnContext +SchXMLTableColumnContext::SchXMLTableColumnContext( + SvXMLImport& rImport, + SchXMLTable& aTable ) : + SvXMLImportContext( rImport ), + mrTable( aTable ) +{ +} + +void SchXMLTableColumnContext::startFastElement (sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + // get number-columns-repeated attribute + sal_Int32 nRepeated = 1; + bool bHidden = false; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(TABLE, XML_NUMBER_COLUMNS_REPEATED): + { + if( !aIter.isEmpty()) + nRepeated = aIter.toInt32(); + break; + } + case XML_ELEMENT(TABLE, XML_VISIBILITY): + { + OUString aVisibility = aIter.toString(); + bHidden = aVisibility == GetXMLToken( XML_COLLAPSE ); + break; + } + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + sal_Int32 nOldCount = mrTable.nNumberOfColsEstimate; + sal_Int32 nNewCount = nOldCount + nRepeated; + mrTable.nNumberOfColsEstimate = nNewCount; + + if( bHidden ) + { + //i91578 display of hidden values (copy paste scenario; use hidden flag during migration to locale table upon paste ) + sal_Int32 nColOffset = ( mrTable.bHasHeaderColumn ? 1 : 0 ); + for( sal_Int32 nN = nOldCount; nN<nNewCount; nN++ ) + { + sal_Int32 nHiddenColumnIndex = nN-nColOffset; + if( nHiddenColumnIndex>=0 ) + mrTable.aHiddenColumns.push_back(nHiddenColumnIndex); + } + } +} + +SchXMLTableColumnContext::~SchXMLTableColumnContext() +{ +} + +// classes for rows +// class SchXMLTableRowsContext +SchXMLTableRowsContext::SchXMLTableRowsContext( + SvXMLImport& rImport, + SchXMLTable& aTable ) : + SvXMLImportContext( rImport ), + mrTable( aTable ) +{ +} + +SchXMLTableRowsContext::~SchXMLTableRowsContext() +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableRowsContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + + if( nElement == XML_ELEMENT(TABLE, XML_TABLE_ROW) ) + pContext = new SchXMLTableRowContext( GetImport(), mrTable ); + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + + return pContext; +} + +// class SchXMLTableRowContext +SchXMLTableRowContext::SchXMLTableRowContext( + SvXMLImport& rImport, + SchXMLTable& aTable ) : + SvXMLImportContext( rImport ), + mrTable( aTable ) +{ + mrTable.nColumnIndex = -1; + mrTable.nRowIndex++; + + std::vector< SchXMLCell > aNewRow; + aNewRow.reserve( mrTable.nNumberOfColsEstimate ); + while( mrTable.aData.size() <= o3tl::make_unsigned(mrTable.nRowIndex) ) + mrTable.aData.push_back( aNewRow ); +} + +SchXMLTableRowContext::~SchXMLTableRowContext() +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableRowContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + + // <table:table-cell> element + if( nElement == XML_ELEMENT(TABLE, XML_TABLE_CELL) ) + { + pContext = new SchXMLTableCellContext( GetImport(), mrTable ); + } + else + { + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + assert(false); + } + + return pContext; +} + +namespace { + +class SchXMLRangeSomewhereContext : public SvXMLImportContext +{ +//#i113950# previously the range was exported to attribute text:id, +//but that attribute does not allow arbitrary strings anymore +//so we need to find an alternative to save that range info for copy/paste scenario ... +//-> use description at an empty group element for now + +private: + OUString& mrRangeString; + OUStringBuffer maRangeStringBuffer; + +public: + SchXMLRangeSomewhereContext( SvXMLImport& rImport, + OUString& rRangeString ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +} + +// classes for cells and their content +// class SchXMLTableCellContext +SchXMLTableCellContext::SchXMLTableCellContext( + SvXMLImport& rImport, + SchXMLTable& aTable) + : SvXMLImportContext(rImport) + , mrTable(aTable) + , mbReadText(false) +{ +} + +SchXMLTableCellContext::~SchXMLTableCellContext() +{ +} + +void SchXMLTableCellContext::startFastElement (sal_Int32 /*nElement*/, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList) +{ + OUString aCellContent; + SchXMLCellType eValueType = SCH_CELL_TYPE_UNKNOWN; + + for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) + { + switch(aIter.getToken()) + { + case XML_ELEMENT(OFFICE, XML_VALUE_TYPE): + if( IsXMLToken( aIter, XML_FLOAT ) ) + eValueType = SCH_CELL_TYPE_FLOAT; + else if( IsXMLToken( aIter, XML_STRING ) ) + eValueType = SCH_CELL_TYPE_STRING; + break; + + case XML_ELEMENT(OFFICE, XML_VALUE): + aCellContent = aIter.toString(); + break; + + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } + + mbReadText = true; + SchXMLCell aCell; + aCell.eType = eValueType; + + if( eValueType == SCH_CELL_TYPE_FLOAT ) + { + double fData; + // the result may be false if a NaN is read, but that's ok + ::sax::Converter::convertDouble( fData, aCellContent ); + + aCell.fValue = fData; + // don't read text from following <text:p> or <text:list> element + mbReadText = false; + } + + mrTable.aData[ mrTable.nRowIndex ].push_back( aCell ); + mrTable.nColumnIndex++; + if( mrTable.nMaxColumnIndex < mrTable.nColumnIndex ) + mrTable.nMaxColumnIndex = mrTable.nColumnIndex; +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTableCellContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + + // <text:list> element + if( nElement == XML_ELEMENT(TEXT, XML_LIST ) && mbReadText ) + { + SchXMLCell& rCell = mrTable.aData[ mrTable.nRowIndex ][ mrTable.nColumnIndex ]; + rCell.aComplexString = Sequence< OUString >(); + rCell.eType = SCH_CELL_TYPE_COMPLEX_STRING; + pContext = new SchXMLTextListContext( GetImport(), rCell.aComplexString ); + mbReadText = false;//don't apply text from <text:p> + } + // <text:p> element - read text (and range from text:id old version) + else if( nElement == XML_ELEMENT(TEXT, XML_P) || + nElement == XML_ELEMENT(LO_EXT, XML_P) ) + { + pContext = new SchXMLParagraphContext( GetImport(), maCellContent, &maRangeId ); + } + // <draw:g> element - read range + else if( nElement == XML_ELEMENT(DRAW, XML_G) ) + { + //#i113950# previously the range was exported to attribute text:id, but that attribute does not allow arbitrary strings anymore + //so we need to find an alternative to save that range info for copy/paste scenario ... -> use description at an empty group element for now + pContext = new SchXMLRangeSomewhereContext( GetImport(), maRangeId ); + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + + return pContext; +} + +void SchXMLTableCellContext::endFastElement(sal_Int32 ) +{ + if( mbReadText && !maCellContent.isEmpty() ) //apply text from <text:p> element + mrTable.aData[ mrTable.nRowIndex ][ mrTable.nColumnIndex ].aString = maCellContent; + if( !maRangeId.isEmpty()) + mrTable.aData[ mrTable.nRowIndex ][ mrTable.nColumnIndex ].aRangeId = maRangeId; +} + +static void lcl_ApplyCellToComplexLabel( const SchXMLCell& rCell, Sequence< uno::Any >& rComplexLabel ) +{ + if( rCell.eType == SCH_CELL_TYPE_STRING ) + { + rComplexLabel = { uno::Any(rCell.aString) }; + } + else if( rCell.aComplexString.hasElements() && rCell.eType == SCH_CELL_TYPE_COMPLEX_STRING ) + { + sal_Int32 nCount = rCell.aComplexString.getLength(); + rComplexLabel.realloc( nCount ); + auto pComplexLabel = rComplexLabel.getArray(); + for( sal_Int32 nN=0; nN<nCount; nN++) + pComplexLabel[nN] <<= (rCell.aComplexString)[nN]; + } + else if( rCell.eType == SCH_CELL_TYPE_FLOAT ) + { + rComplexLabel = { uno::Any(rCell.fValue) }; + } +} + +void SchXMLTableHelper::applyTableToInternalDataProvider( + const SchXMLTable& rTable, + const uno::Reference< chart2::XChartDocument >& xChartDoc ) +{ + // apply all data read from the local table to the internal data provider + if( !xChartDoc.is() || !xChartDoc->hasInternalDataProvider() ) + return; + Reference< chart2::data::XDataProvider > xDataProv( xChartDoc->getDataProvider() ); + if( !xDataProv.is() ) + return; + + //prepare the read local table data + sal_Int32 nNumRows( static_cast< sal_Int32 >( rTable.aData.size())); + sal_Int32 nRowOffset = 0; + if( rTable.bHasHeaderRow ) + { + --nNumRows; + nRowOffset = 1; + } + sal_Int32 nNumColumns( rTable.nMaxColumnIndex + 1 ); + sal_Int32 nColOffset = 0; + if( rTable.bHasHeaderColumn ) + { + --nNumColumns; + nColOffset = 1; + } + + Sequence< Sequence< double > > aDataInRows( nNumRows ); + auto aDataInRowsRange = asNonConstRange(aDataInRows); + Sequence< Sequence< uno::Any > > aComplexRowDescriptions( nNumRows ); + auto aComplexRowDescriptionsRange = asNonConstRange(aComplexRowDescriptions); + Sequence< Sequence< uno::Any > > aComplexColumnDescriptions( nNumColumns ); + auto aComplexColumnDescriptionsRange = asNonConstRange(aComplexColumnDescriptions); + for( sal_Int32 i=0; i<nNumRows; ++i ) + aDataInRowsRange[i].realloc( nNumColumns ); + + if( !rTable.aData.empty() ) + { + //apply column labels + if( rTable.bHasHeaderRow ) + { + const ::std::vector< SchXMLCell >& rFirstRow = rTable.aData.front(); + const sal_Int32 nColumnLabelsSize = aComplexColumnDescriptions.getLength(); + const sal_Int32 nMax = ::std::min< sal_Int32 >( nColumnLabelsSize, static_cast< sal_Int32 >( rFirstRow.size()) - nColOffset ); + SAL_WARN_IF( nMax != nColumnLabelsSize, "xmloff.chart", "nMax != nColumnLabelsSize"); + for( sal_Int32 i=0; i<nMax; ++i ) + lcl_ApplyCellToComplexLabel( rFirstRow[i+nColOffset], aComplexColumnDescriptionsRange[i] ); + } + + std::vector< ::std::vector< SchXMLCell > >::const_iterator aRowIter( rTable.aData.begin() + nRowOffset ); + std::vector< ::std::vector< SchXMLCell > >::const_iterator aEnd( rTable.aData.end() ); + for( sal_Int32 nRow = 0; aRowIter != aEnd && nRow < nNumRows; ++aRowIter, ++nRow ) + { + const ::std::vector< SchXMLCell >& rRow = *aRowIter; + if( !rRow.empty() ) + { + // row label + if( rTable.bHasHeaderColumn ) + lcl_ApplyCellToComplexLabel( rRow.front(), aComplexRowDescriptionsRange[nRow] ); + + // values + Sequence< double >& rTargetRow = aDataInRowsRange[nRow]; + auto pTargetRow = rTargetRow.getArray(); + lcl_ApplyCellToData aApplyCellToData = ::std::for_each( rRow.begin() + nColOffset, rRow.end(), lcl_ApplyCellToData( rTargetRow ) ); + for( sal_Int32 nCurrentIndex = aApplyCellToData.getCurrentIndex(); nCurrentIndex<nNumColumns; nCurrentIndex++ ) + pTargetRow[nCurrentIndex] = std::numeric_limits<double>::quiet_NaN();//#i110615# + } + } + } + + //apply the collected data to the chart + Reference< chart2::XAnyDescriptionAccess > xDataAccess( xDataProv, uno::UNO_QUERY ); + if( !xDataAccess.is() ) + return; + + xDataAccess->setData( aDataInRows ); + if( rTable.bHasHeaderColumn ) + xDataAccess->setAnyRowDescriptions( aComplexRowDescriptions ); + if( rTable.bHasHeaderRow ) + xDataAccess->setAnyColumnDescriptions( aComplexColumnDescriptions ); + + if ( rTable.bProtected ) + { + try + { + Reference< beans::XPropertySet > xProps( xChartDoc, uno::UNO_QUERY_THROW ); + xProps->setPropertyValue( "DisableDataTableDialog", uno::Any( true ) ); + xProps->setPropertyValue( "DisableComplexChartTypes", uno::Any( true ) ); + } + catch ( uno::Exception& ) + { + } + } +} + +void SchXMLTableHelper::switchRangesFromOuterToInternalIfNecessary( + const SchXMLTable& rTable, + const tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + const uno::Reference< chart2::XChartDocument >& xChartDoc, + chart::ChartDataRowSource eDataRowSource ) +{ + if( ! (xChartDoc.is() && xChartDoc->hasInternalDataProvider())) + return; + + // If the range-strings are valid (starting with "local-table") they should + // be interpreted like given, otherwise (when the ranges refer to Calc- or + // Writer-ranges, but the container is not available like when pasting a + // chart from Calc to Impress) the range is ignored, and every object gets + // one table column in the order of appearance, which is: 1. categories, + // 2. data series: 2.a) domains, 2.b) values (main-role, usually y-values) + + Reference< chart2::data::XDataProvider > xDataProv( xChartDoc->getDataProvider()); + + // create a mapping from original ranges to new ranges + lcl_tOriginalRangeToInternalRangeMap aRangeMap; + + lcl_fillRangeMapping( rTable, aRangeMap, eDataRowSource ); + + const OUString lcl_aCategoriesRange(aCategoriesRange); + + bool bCategoriesApplied = false; + // translate ranges (using the map created before) + for( const auto& rLSeq : rLSequencesPerIndex ) + { + if( rLSeq.second.is()) + { + // values/error bars/categories + if( rLSeq.first.second == SCH_XML_PART_VALUES || + rLSeq.first.second == SCH_XML_PART_ERROR_BARS ) + { + Reference< chart2::data::XDataSequence > xSeq( rLSeq.second->getValues()); + + OUString aRange; + if( xSeq.is() && + SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq, aRange, /* bClearProp = */ true ) && + lcl_mapContainsRange( aRangeMap, aRange )) + { + Reference< chart2::data::XDataSequence > xNewSeq( + lcl_reassignDataSequence( xSeq, xDataProv, aRangeMap, aRange )); + if( xNewSeq != xSeq ) + { + SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ), + Reference< beans::XPropertySet >( xNewSeq, uno::UNO_QUERY )); + rLSeq.second->setValues( xNewSeq ); + } + } + else + { + if( lcl_tableOfRangeMatches( aRange, rTable.aTableNameOfFile )) + { + if( rLSeq.first.first == SCH_XML_CATEGORIES_INDEX ) + bCategoriesApplied = true; + } + else + { + if( rLSeq.first.first == SCH_XML_CATEGORIES_INDEX ) + { + Reference< beans::XPropertySet > xOldSequenceProp( rLSeq.second->getValues(), uno::UNO_QUERY ); + Reference< chart2::data::XDataSequence > xNewSequence( + xDataProv->createDataSequenceByRangeRepresentation("categories")); + SchXMLTools::copyProperties( + xOldSequenceProp, Reference< beans::XPropertySet >( xNewSequence, uno::UNO_QUERY )); + rLSeq.second->setValues( xNewSequence ); + bCategoriesApplied = true; + } + else + { + Reference< beans::XPropertySet > xOldSequenceProp( rLSeq.second->getValues(), uno::UNO_QUERY ); + OUString aRep( OUString::number( rLSeq.first.first )); + Reference< chart2::data::XDataSequence > xNewSequence( + xDataProv->createDataSequenceByRangeRepresentation( aRep )); + SchXMLTools::copyProperties( + xOldSequenceProp, Reference< beans::XPropertySet >( xNewSequence, uno::UNO_QUERY )); + rLSeq.second->setValues( xNewSequence ); + } + } + } + } + else // labels + { + SAL_WARN_IF( rLSeq.first.second != SCH_XML_PART_LABEL, "xmloff.chart", "rLSeq.first.second != SCH_XML_PART_LABEL" ); + // labels + Reference< chart2::data::XDataSequence > xSeq( rLSeq.second->getLabel()); + OUString aRange; + if( xSeq.is() && + SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq, aRange, /* bClearProp = */ true ) && + lcl_mapContainsRange( aRangeMap, aRange )) + { + Reference< chart2::data::XDataSequence > xNewSeq( + lcl_reassignDataSequence( xSeq, xDataProv, aRangeMap, aRange )); + if( xNewSeq != xSeq ) + { + SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ), + Reference< beans::XPropertySet >( xNewSeq, uno::UNO_QUERY )); + rLSeq.second->setLabel( xNewSeq ); + } + } + else if( ! lcl_tableOfRangeMatches( aRange, rTable.aTableNameOfFile )) + { + OUString aRep = "label " + OUString::number( rLSeq.first.first ); + + Reference< chart2::data::XDataSequence > xNewSeq( + xDataProv->createDataSequenceByRangeRepresentation( aRep )); + SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ), + Reference< beans::XPropertySet >( xNewSeq, uno::UNO_QUERY )); + rLSeq.second->setLabel( xNewSeq ); + } + } + } + } + + // there exist files with own data without a categories element but with row + // descriptions. The row descriptions were used as categories even without + // the categories element + if( ! bCategoriesApplied ) + { + SchXMLTools::CreateCategories( + xDataProv, xChartDoc, "categories", + 0 /* nCooSysIndex */, 0 /* nDimension */ ); + } + + //i91578 display of hidden values (copy paste scenario; use hidden flag during migration to locale table upon paste ) + //remove series that consist only of hidden columns + Reference< chart2::XInternalDataProvider > xInternalDataProvider( xDataProv, uno::UNO_QUERY ); + if( !xInternalDataProvider.is() || rTable.aHiddenColumns.empty() ) + return; + + try + { + Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xChartDoc->getFirstDiagram(), uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() ); + for( const auto& rCooSys : aCooSysSeq ) + { + Reference< chart2::XChartTypeContainer > xCooSysContainer( rCooSys, uno::UNO_QUERY_THROW ); + const Sequence< Reference< chart2::XChartType > > aChartTypeSeq( xCooSysContainer->getChartTypes()); + for( const auto& rChartType : aChartTypeSeq ) + { + Reference< chart2::XDataSeriesContainer > xSeriesContainer( rChartType, uno::UNO_QUERY ); + if(!xSeriesContainer.is()) + continue; + const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xSeriesContainer->getDataSeries() ); + std::vector< Reference< chart2::XDataSeries > > aRemainingSeries; + + for( const auto& rSeries : aSeriesSeq ) + { + Reference< chart2::data::XDataSource > xDataSource( rSeries, uno::UNO_QUERY ); + if( xDataSource.is() ) + { + bool bHasUnhiddenColumns = false; + OUString aRange; + const uno::Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences( xDataSource->getDataSequences() ); + for( const auto& xLabeledSequence : aSequences ) + { + if(!xLabeledSequence.is()) + continue; + Reference< chart2::data::XDataSequence > xValues( xLabeledSequence->getValues() ); + if( xValues.is() ) + { + aRange = xValues->getSourceRangeRepresentation(); + if( ::std::find( rTable.aHiddenColumns.begin(), rTable.aHiddenColumns.end(), aRange.toInt32() ) == rTable.aHiddenColumns.end() ) + bHasUnhiddenColumns = true; + } + if( !bHasUnhiddenColumns ) + { + Reference< chart2::data::XDataSequence > xLabel( xLabeledSequence->getLabel() ); + if( xLabel.is() ) + { + aRange = xLabel->getSourceRangeRepresentation(); + const sal_Int32 nId = o3tl::toInt32(o3tl::getToken(aRange, 1, ' ')); + if( ::std::find( rTable.aHiddenColumns.begin(), rTable.aHiddenColumns.end(), nId ) == rTable.aHiddenColumns.end() ) + bHasUnhiddenColumns = true; + } + } + } + if( bHasUnhiddenColumns ) + aRemainingSeries.push_back( rSeries ); + } + } + + if( aRemainingSeries.size() != o3tl::make_unsigned(aSeriesSeq.getLength()) ) + { + //remove the series that have only hidden data + xSeriesContainer->setDataSeries( comphelper::containerToSequence(aRemainingSeries) ); + + //remove unused sequences + Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY ); + if( xDataSource.is() ) + { + //first detect which columns are really used + std::map< sal_Int32, bool > aUsageMap; + OUString aRange; + const Sequence< Reference< chart2::data::XLabeledDataSequence > > aUsedSequences( xDataSource->getDataSequences() ); + for( const auto& xLabeledSequence : aUsedSequences ) + { + if(!xLabeledSequence.is()) + continue; + Reference< chart2::data::XDataSequence > xValues( xLabeledSequence->getValues() ); + if( xValues.is() ) + { + aRange = xValues->getSourceRangeRepresentation(); + sal_Int32 nIndex = aRange.toInt32(); + if( nIndex!=0 || aRange != lcl_aCategoriesRange ) + aUsageMap[nIndex] = true; + } + Reference< chart2::data::XDataSequence > xLabel( xLabeledSequence->getLabel() ); + if( xLabel.is() ) + { + aRange = xLabel->getSourceRangeRepresentation(); + std::u16string_view aSecondToken = o3tl::getToken(aRange, 1, ' '); + if( !aSecondToken.empty() ) + aUsageMap[o3tl::toInt32(aSecondToken)] = true; + } + } + + ::std::vector< sal_Int32 > aSequenceIndexesToDelete; + std::copy_if(rTable.aHiddenColumns.begin(), rTable.aHiddenColumns.end(), + std::back_inserter(aSequenceIndexesToDelete), + [&aUsageMap](sal_Int32 nSequenceIndex) { return aUsageMap.find(nSequenceIndex) == aUsageMap.end(); }); + + // delete unnecessary sequences of the internal data + // iterate using greatest index first, so that deletion does not + // shift other sequences that will be deleted later + ::std::sort( aSequenceIndexesToDelete.begin(), aSequenceIndexesToDelete.end()); + for( ::std::vector< sal_Int32 >::reverse_iterator aIt( + aSequenceIndexesToDelete.rbegin()); aIt != aSequenceIndexesToDelete.rend(); ++aIt ) + { + if( *aIt != -1 ) + xInternalDataProvider->deleteSequence( *aIt ); + } + } + } + } + } + } + catch( const uno::Exception & ) + { + } +} + +SchXMLRangeSomewhereContext::SchXMLRangeSomewhereContext( SvXMLImport& rImport, + OUString& rRangeString ) : + SvXMLImportContext( rImport ), + mrRangeString( rRangeString ) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLRangeSomewhereContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + if( nElement == XML_ELEMENT(SVG, XML_DESC) + || nElement == XML_ELEMENT(SVG_COMPAT, XML_DESC) ) + { + return new XMLStringBufferImportContext( GetImport(), maRangeStringBuffer ); + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return nullptr; +} + +void SchXMLRangeSomewhereContext::endFastElement(sal_Int32 ) +{ + mrRangeString = maRangeStringBuffer.makeStringAndClear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLTableContext.hxx b/xmloff/source/chart/SchXMLTableContext.hxx new file mode 100644 index 000000000..cd8a6443b --- /dev/null +++ b/xmloff/source/chart/SchXMLTableContext.hxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlictxt.hxx> +#include <xmloff/SchXMLImportHelper.hxx> +#include <com/sun/star/uno/Sequence.hxx> + +#include <com/sun/star/chart/ChartDataRowSource.hpp> + +#include "transporttypes.hxx" + +namespace com::sun::star { + namespace xml::sax { + class XAttributeList; + } + namespace chart { + class XChartDocument; + } +} + +class SchXMLTableContext : public SvXMLImportContext +{ +private: + SchXMLTable& mrTable; + + bool mbHasRowPermutation; + bool mbHasColumnPermutation; + css::uno::Sequence< sal_Int32 > maRowPermutation; + css::uno::Sequence< sal_Int32 > maColumnPermutation; + +public: + SchXMLTableContext( SvXMLImport& rImport, + SchXMLTable& aTable ); + virtual ~SchXMLTableContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + void setRowPermutation( const css::uno::Sequence< sal_Int32 > & rPermutation ); + void setColumnPermutation( const css::uno::Sequence< sal_Int32 > & rPermutation ); +}; + +class SchXMLTableHelper +{ +public: + static void applyTableToInternalDataProvider( const SchXMLTable& rTable, + const css::uno::Reference< css::chart2::XChartDocument >& xChartDoc ); + + /** This function reorders local data to fit the correct data structure. + Call it after the data series got their styles set. + */ + static void switchRangesFromOuterToInternalIfNecessary( const SchXMLTable& rTable, + const tSchXMLLSequencesPerIndex & rLSequencesPerIndex, + const css::uno::Reference< css::chart2::XChartDocument >& xChartDoc, + css::chart::ChartDataRowSource eDataRowSource ); +}; + +// classes for columns + +/** With this context all column elements are parsed to + determine the index of the column containing + the row descriptions and probably get an estimate + for the altogether number of columns + */ +class SchXMLTableColumnsContext : public SvXMLImportContext +{ +private: + SchXMLTable& mrTable; + +public: + SchXMLTableColumnsContext( SvXMLImport& rImport, + SchXMLTable& aTable ); + virtual ~SchXMLTableColumnsContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +class SchXMLTableColumnContext : public SvXMLImportContext +{ +private: + SchXMLTable& mrTable; + +public: + SchXMLTableColumnContext( SvXMLImport& rImport, + SchXMLTable& aTable ); + virtual ~SchXMLTableColumnContext() override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +// classes for rows + +class SchXMLTableRowsContext : public SvXMLImportContext +{ +private: + SchXMLTable& mrTable; + +public: + SchXMLTableRowsContext( SvXMLImport& rImport, + SchXMLTable& aTable ); + virtual ~SchXMLTableRowsContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +class SchXMLTableRowContext : public SvXMLImportContext +{ +private: + SchXMLTable& mrTable; + +public: + SchXMLTableRowContext( SvXMLImport& rImport, + SchXMLTable& aTable ); + virtual ~SchXMLTableRowContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +// classes for cells and their content + +class SchXMLTableCellContext : public SvXMLImportContext +{ +private: + SchXMLTable& mrTable; + OUString maCellContent; + OUString maRangeId; + bool mbReadText; + +public: + SchXMLTableCellContext( SvXMLImport& rImport, + SchXMLTable& aTable ); + virtual ~SchXMLTableCellContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLTextListContext.cxx b/xmloff/source/chart/SchXMLTextListContext.cxx new file mode 100644 index 000000000..da70c24ed --- /dev/null +++ b/xmloff/source/chart/SchXMLTextListContext.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLTextListContext.hxx" +#include "SchXMLParagraphContext.hxx" + +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlictxt.hxx> +#include <sal/log.hxx> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using namespace com::sun::star; +using namespace ::xmloff::token; + +namespace { + +class SchXMLListItemContext : public SvXMLImportContext +{ +public: + SchXMLListItemContext( SvXMLImport& rImport, OUString& rText ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + +private: + OUString& m_rText; +}; + +} + +SchXMLListItemContext::SchXMLListItemContext( + SvXMLImport& rImport + , OUString& rText ) + : SvXMLImportContext( rImport ) + , m_rText( rText ) +{ +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLListItemContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + if( nElement == XML_ELEMENT(TEXT, XML_P) || + nElement == XML_ELEMENT(LO_EXT, XML_P) ) + pContext = new SchXMLParagraphContext( GetImport(), m_rText ); + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return pContext; +} + +SchXMLTextListContext::SchXMLTextListContext( + SvXMLImport& rImport + , Sequence< OUString>& rTextList ) + : SvXMLImportContext( rImport ) + , m_rTextList( rTextList ) +{ +} + +SchXMLTextListContext::~SchXMLTextListContext() +{ +} + +void SchXMLTextListContext::endFastElement(sal_Int32 ) +{ + sal_Int32 nCount = m_aTextVector.size(); + m_rTextList.realloc(nCount); + auto pTextList = m_rTextList.getArray(); + for( sal_Int32 nN=0; nN<nCount; nN++ ) + pTextList[nN]=m_aTextVector[nN]; +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTextListContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& ) +{ + SvXMLImportContext* pContext = nullptr; + if( nElement == XML_ELEMENT(TEXT, XML_LIST_ITEM) ) + { + m_aTextVector.emplace_back( ); + pContext = new SchXMLListItemContext( GetImport(), m_aTextVector.back() ); + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return pContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLTextListContext.hxx b/xmloff/source/chart/SchXMLTextListContext.hxx new file mode 100644 index 000000000..32d9bd3e3 --- /dev/null +++ b/xmloff/source/chart/SchXMLTextListContext.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlictxt.hxx> +#include <rtl/ustring.hxx> +#include <vector> + +namespace com::sun::star::xml::sax { + class XAttributeList; +} + +class SchXMLTextListContext : public SvXMLImportContext +{ +public: + SchXMLTextListContext( SvXMLImport& rImport, + css::uno::Sequence< OUString>& rTextList ); + virtual ~SchXMLTextListContext() override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + +private: + css::uno::Sequence< OUString>& m_rTextList; + std::vector< OUString> m_aTextVector; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLTools.cxx b/xmloff/source/chart/SchXMLTools.cxx new file mode 100644 index 000000000..8fbf4aad7 --- /dev/null +++ b/xmloff/source/chart/SchXMLTools.cxx @@ -0,0 +1,858 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SchXMLTools.hxx" + +#include <rtl/ustrbuf.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmlement.hxx> +#include <xmloff/xmlimppr.hxx> +#include <xmloff/prstylei.hxx> +#include <xmloff/xmlprmap.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlmetai.hxx> +#include <xmloff/maptype.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/chart2/data/LabeledDataSequence.hpp> +#include <com/sun/star/chart2/data/XDataProvider.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp> +#include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/xml/sax/XDocumentHandler.hpp> + +#include <comphelper/processfactory.hxx> +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> +#include <algorithm> +#include <map> + +using namespace com::sun::star; +using namespace ::xmloff::token; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +namespace +{ + +OUString lcl_getGeneratorFromModel( const uno::Reference< frame::XModel >& xChartModel ) +{ + OUString aGenerator; + uno::Reference< document::XDocumentPropertiesSupplier> xChartDocumentPropertiesSupplier( xChartModel, uno::UNO_QUERY ); + if( xChartDocumentPropertiesSupplier.is() ) + { + uno::Reference< document::XDocumentProperties > xChartDocumentProperties( + xChartDocumentPropertiesSupplier->getDocumentProperties()); + if( xChartDocumentProperties.is() ) + aGenerator = xChartDocumentProperties->getGenerator(); + } + return aGenerator; +} + +OUString lcl_getGeneratorFromModelOrItsParent( const uno::Reference< frame::XModel >& xChartModel ) +{ + OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) ); + if( aGenerator.isEmpty() ) //try to get the missing info from the parent document + { + uno::Reference< container::XChild > xChild( xChartModel, uno::UNO_QUERY ); + if( xChild.is() ) + aGenerator = lcl_getGeneratorFromModel( uno::Reference< frame::XModel >( xChild->getParent(), uno::UNO_QUERY) ); + } + return aGenerator; +} + +sal_Int32 lcl_getBuildIDFromGenerator( std::u16string_view rGenerator ) +{ + //returns -1 if nothing found + sal_Int32 nBuildId = -1; + static const OUStringLiteral sBuildCompare( u"$Build-" ); + size_t nBegin = rGenerator.find( sBuildCompare ); + if( nBegin != std::u16string_view::npos ) + { + std::u16string_view sBuildId = rGenerator.substr( nBegin + sBuildCompare.getLength() ); + nBuildId = o3tl::toInt32(sBuildId); + } + return nBuildId; +} + +OUString lcl_ConvertRange( const OUString & rRange, const Reference< chart2::data::XDataProvider >& xDataProvider ) +{ + OUString aResult = rRange; + Reference< chart2::data::XRangeXMLConversion > xRangeConversion( xDataProvider, uno::UNO_QUERY ); + if( xRangeConversion.is()) + aResult = xRangeConversion->convertRangeFromXML( rRange ); + return aResult; +} + +Reference< chart2::data::XDataSequence > lcl_createNewSequenceFromCachedXMLRange( const Reference< chart2::data::XDataSequence >& xSeq, const Reference< chart2::data::XDataProvider >& xDataProvider ) +{ + Reference< chart2::data::XDataSequence > xRet; + OUString aRange; + if( xSeq.is() && SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq, aRange, /* bClearProp = */ true ) ) + { + xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( + lcl_ConvertRange( aRange, xDataProvider )) ); + SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ), + Reference< beans::XPropertySet >( xRet, uno::UNO_QUERY )); + } + return xRet; +} + +} // anonymous namespace + +namespace SchXMLTools +{ + +const SvXMLEnumMapEntry<SchXMLChartTypeEnum> aXMLChartClassMap[] = +{ + { XML_LINE, XML_CHART_CLASS_LINE }, + { XML_AREA, XML_CHART_CLASS_AREA }, + { XML_CIRCLE, XML_CHART_CLASS_CIRCLE }, + { XML_RING, XML_CHART_CLASS_RING }, + { XML_SCATTER, XML_CHART_CLASS_SCATTER }, + { XML_RADAR, XML_CHART_CLASS_RADAR }, + { XML_FILLED_RADAR, XML_CHART_CLASS_FILLED_RADAR }, + { XML_BAR, XML_CHART_CLASS_BAR }, + { XML_STOCK, XML_CHART_CLASS_STOCK }, + { XML_BUBBLE, XML_CHART_CLASS_BUBBLE }, + { XML_SURFACE, XML_CHART_CLASS_BAR }, //@todo change this if a surface chart is available + { XML_ADD_IN, XML_CHART_CLASS_ADDIN }, + { XML_TOKEN_INVALID, XML_CHART_CLASS_UNKNOWN } +}; + +SchXMLChartTypeEnum GetChartTypeEnum( std::u16string_view rClassName ) +{ + SchXMLChartTypeEnum nEnumVal = XML_CHART_CLASS_UNKNOWN; + SvXMLUnitConverter::convertEnum( nEnumVal, rClassName, aXMLChartClassMap ); + return nEnumVal; +} + +typedef std::map< OUString, OUString > tMakeStringStringMap; +//static +static const tMakeStringStringMap& lcl_getChartTypeNameMap() +{ + //shape property -- chart model object property + static const tMakeStringStringMap g_aChartTypeNameMap{ + {"com.sun.star.chart.LineDiagram", + "com.sun.star.chart2.LineChartType"}, + {"com.sun.star.chart.AreaDiagram", + "com.sun.star.chart2.AreaChartType"}, + {"com.sun.star.chart.BarDiagram", + "com.sun.star.chart2.ColumnChartType"}, + {"com.sun.star.chart.PieDiagram", + "com.sun.star.chart2.PieChartType"}, + {"com.sun.star.chart.DonutDiagram", + "com.sun.star.chart2.DonutChartType"}, + {"com.sun.star.chart.XYDiagram", + "com.sun.star.chart2.ScatterChartType"}, + {"com.sun.star.chart.NetDiagram", + "com.sun.star.chart2.NetChartType"}, + {"com.sun.star.chart.FilledNetDiagram", + "com.sun.star.chart2.FilledNetChartType"}, + {"com.sun.star.chart.StockDiagram", + "com.sun.star.chart2.CandleStickChartType"}, + {"com.sun.star.chart.BubbleDiagram", + "com.sun.star.chart2.BubbleChartType"}}; + return g_aChartTypeNameMap; +} + +OUString GetNewChartTypeName( const OUString & rOldChartTypeName ) +{ + OUString aNew(rOldChartTypeName); + + const tMakeStringStringMap& rMap = lcl_getChartTypeNameMap(); + tMakeStringStringMap::const_iterator aIt( rMap.find( rOldChartTypeName )); + if( aIt != rMap.end()) + { + aNew = aIt->second; + } + return aNew; +} + +OUString GetChartTypeByClassName( + std::u16string_view rClassName, bool bUseOldNames ) +{ + OUStringBuffer aResultBuffer; + bool bInternalType = false; + + if( bUseOldNames ) + aResultBuffer.append( "com.sun.star.chart."); + else + aResultBuffer.append( "com.sun.star.chart2."); + + bInternalType = true; + + if( IsXMLToken( rClassName, XML_LINE )) + aResultBuffer.append("Line"); + else if( IsXMLToken( rClassName, XML_AREA )) + aResultBuffer.append("Area"); + else if( IsXMLToken( rClassName, XML_BAR )) + { + if( bUseOldNames ) + aResultBuffer.append("Bar"); + else + { + aResultBuffer.append("Column"); + // @todo: might be Bar + } + } + else if( IsXMLToken( rClassName, XML_CIRCLE )) + aResultBuffer.append("Pie"); + else if( IsXMLToken( rClassName, XML_RING )) + aResultBuffer.append("Donut"); + else if( IsXMLToken( rClassName, XML_SCATTER )) + { + if( bUseOldNames ) + aResultBuffer.append("XY"); + else + aResultBuffer.append("Scatter"); + } + + else if( IsXMLToken( rClassName, XML_BUBBLE )) + aResultBuffer.append("Bubble"); + else if( IsXMLToken( rClassName, XML_RADAR )) + aResultBuffer.append("Net"); + else if( IsXMLToken( rClassName, XML_FILLED_RADAR )) + aResultBuffer.append("FilledNet"); + else if( IsXMLToken( rClassName, XML_STOCK )) + { + if( bUseOldNames ) + aResultBuffer.append("Stock"); + else + aResultBuffer.append("CandleStick"); + } + else if( IsXMLToken( rClassName, XML_SURFACE )) + { + //@todo change this if a surface chart is available + if( bUseOldNames ) + aResultBuffer.append("Bar"); + else + aResultBuffer.append("Column"); + } + else + bInternalType = false; + + if( ! bInternalType ) + return OUString(); + + if( bUseOldNames ) + aResultBuffer.append("Diagram"); + else + aResultBuffer.append("ChartType"); + + return aResultBuffer.makeStringAndClear(); + +} + +XMLTokenEnum getTokenByChartType( + const OUString & rChartTypeService, bool bUseOldNames ) +{ + XMLTokenEnum eResult = XML_TOKEN_INVALID; + OUString aPrefix, aPostfix; + + if( bUseOldNames ) + { + aPrefix = "com.sun.star.chart."; + aPostfix = "Diagram"; + } + else + { + aPrefix = "com.sun.star.chart2."; + aPostfix = "ChartType"; + } + + if( rChartTypeService.match( aPrefix )) + { + sal_Int32 nSkip = aPrefix.getLength(); + SAL_WARN_IF( rChartTypeService.getLength() < nSkip, "xmloff.chart", "ChartTypeService.getLength() < nSkip" ); + sal_Int32 nTypeLength = rChartTypeService.getLength() - nSkip - aPostfix.getLength(); + // if postfix matches and leaves a non-empty type + if( nTypeLength > 0 && rChartTypeService.match( aPostfix, nSkip + nTypeLength )) + { + std::u16string_view aServiceName( rChartTypeService.subView( nSkip, nTypeLength )); + + if ( aServiceName == u"Line" ) + eResult = XML_LINE; + else if ( aServiceName == u"Area" ) + eResult = XML_AREA; + else if( aServiceName == u"Bar" || + (!bUseOldNames && aServiceName == u"Column")) + eResult = XML_BAR; + else if ( aServiceName == u"Pie" ) + eResult = XML_CIRCLE; + else if ( aServiceName == u"Donut" ) + eResult = XML_RING; + else if( (bUseOldNames && aServiceName == u"XY") || + (!bUseOldNames && aServiceName == u"Scatter")) + eResult = XML_SCATTER; + else if ( aServiceName == u"Bubble" ) + eResult = XML_BUBBLE; + else if ( aServiceName == u"Net" ) + eResult = XML_RADAR; + else if ( aServiceName == u"FilledNet" ) + eResult = XML_FILLED_RADAR; + else if( (bUseOldNames && aServiceName == u"Stock") || + (!bUseOldNames && aServiceName == u"CandleStick")) + eResult = XML_STOCK; + } + } + + if( eResult == XML_TOKEN_INVALID && !rChartTypeService.isEmpty() ) + eResult = XML_ADD_IN; + + return eResult; +} + +Reference< chart2::data::XLabeledDataSequence2 > GetNewLabeledDataSequence() +{ + Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() ); + Reference< chart2::data::XLabeledDataSequence2 > xResult = chart2::data::LabeledDataSequence::create(xContext); + return xResult; +} + +Reference< chart2::data::XDataSequence > CreateDataSequence( + const OUString & rRange, + const Reference< chart2::XChartDocument >& xChartDoc ) +{ + Reference< chart2::data::XDataSequence > xRet; + + if( !xChartDoc.is() ) + { + SAL_WARN("xmloff.chart", "need a chart document" ); + return xRet; + } + + Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() ); + if( !xDataProvider.is() ) + { + SAL_WARN("xmloff.chart", "need a data provider" ); + return xRet; + } + + bool bUseInternal = false; + uno::Reference<beans::XPropertySet> xPropSet(xDataProvider, uno::UNO_QUERY); + if (xPropSet.is()) + { + try + { + bool bVal = false; + uno::Any any = xPropSet->getPropertyValue("UseInternalDataProvider"); + if (any >>= bVal) + bUseInternal = bVal; + } + catch (const beans::UnknownPropertyException&) + { + // Do nothing + } + } + + if (!bUseInternal) + { + try + { + xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( lcl_ConvertRange( rRange, xDataProvider ))); + SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange ); + } + catch( const lang::IllegalArgumentException & ) + { + SAL_WARN("xmloff.chart", "could not create data sequence" ); + } + } + + if( !xRet.is() && !xChartDoc->hasInternalDataProvider() && !rRange.isEmpty() ) + { + //#i103911# switch to internal data in case the parent cannot provide the requested data + xChartDoc->createInternalDataProvider( true /* bCloneExistingData */ ); + xDataProvider = xChartDoc->getDataProvider(); + try + { + xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( lcl_ConvertRange( rRange, xDataProvider ))); + SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange ); + } + catch( const lang::IllegalArgumentException & ) + { + SAL_WARN("xmloff.chart", "could not create data sequence" ); + } + } + return xRet; +} + +Reference< chart2::data::XDataSequence > CreateDataSequenceWithoutConvert( + const OUString & rRange, + const Reference< chart2::XChartDocument >& xChartDoc ) +{ + Reference< chart2::data::XDataSequence > xRet; + + if( !xChartDoc.is() ) + { + SAL_WARN("xmloff.chart", "need a chart document" ); + return xRet; + } + + Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() ); + if( !xDataProvider.is() ) + { + SAL_WARN("xmloff.chart", "need a data provider" ); + return xRet; + } + + try + { + xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( rRange ) ); + SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange ); + } + catch( const lang::IllegalArgumentException & ) + { + SAL_WARN("xmloff.chart", "could not create data sequence" ); + } + + return xRet; +} + +void CreateCategories( + const uno::Reference< chart2::data::XDataProvider > & xDataProvider, + const uno::Reference< chart2::XChartDocument > & xNewDoc, + const OUString & rRangeAddress, + sal_Int32 nCooSysIndex, + sal_Int32 nDimensionIndex, + tSchXMLLSequencesPerIndex * pLSequencesPerIndex ) +{ + try + { + if( xNewDoc.is() && !rRangeAddress.isEmpty()) + { + if( xDataProvider.is()) + { + uno::Reference< chart2::XDiagram > xDia( xNewDoc->getFirstDiagram()); + if( !xDia.is()) + return; + + uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDia, uno::UNO_QUERY_THROW ); + uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > + aCooSysSeq( xCooSysCnt->getCoordinateSystems()); + if( nCooSysIndex < aCooSysSeq.getLength()) + { + uno::Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIndex] ); + SAL_WARN_IF( !xCooSys.is(), "xmloff.chart", "xCooSys is NULL"); + if( nDimensionIndex < xCooSys->getDimension() ) + { + const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex); + for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI) + { + uno::Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( nDimensionIndex, nI )); + if( xAxis.is() ) + { + chart2::ScaleData aData( xAxis->getScaleData()); + uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( + GetNewLabeledDataSequence(), uno::UNO_QUERY_THROW); + try + { + OUString aConvertedRange( rRangeAddress ); + bool bRangeConverted = false; + if( ! (xNewDoc->hasInternalDataProvider() && aConvertedRange == "categories")) + { + Reference< chart2::data::XRangeXMLConversion > xXMLConv( xDataProvider, uno::UNO_QUERY ); + if( xXMLConv.is()) + { + aConvertedRange = xXMLConv->convertRangeFromXML( rRangeAddress ); + bRangeConverted = true; + } + } + + Reference<chart2::data::XDataSequence> xSequence; + Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xDataProvider, uno::UNO_QUERY); + if (xPivotTableDataProvider.is()) + { + xSequence.set(xPivotTableDataProvider->createDataSequenceOfCategories()); + } + else + { + xSequence.set(xDataProvider->createDataSequenceByRangeRepresentation(aConvertedRange)); + if (bRangeConverted) + setXMLRangePropertyAtDataSequence(xSequence, rRangeAddress); + } + xLabeledSeq->setValues(xSequence); + + } + catch( const lang::IllegalArgumentException & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + aData.Categories.set( xLabeledSeq ); + if( pLSequencesPerIndex ) + { + // register for setting local data if external data provider is not present + pLSequencesPerIndex->emplace( + tSchXMLIndexWithPart( SCH_XML_CATEGORIES_INDEX, SCH_XML_PART_VALUES ), xLabeledSeq ); + } + xAxis->setScaleData( aData ); + } + } + } + } + } + } + } + catch( uno::Exception & ) + { + TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception caught while creating Categories" ); + } +} + +uno::Any getPropertyFromContext( std::u16string_view rPropertyName, const XMLPropStyleContext* pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ) +{ + uno::Any aRet; + if( !pPropStyleContext || !pStylesCtxt ) + return aRet; + const ::std::vector< XMLPropertyState >& rProperties = pPropStyleContext->GetProperties(); + const rtl::Reference< XMLPropertySetMapper >& rMapper = pStylesCtxt->GetImportPropertyMapper( pPropStyleContext->GetFamily()/*XML_STYLE_FAMILY_SCH_CHART_ID*/ )->getPropertySetMapper(); + for( const auto& rProp : rProperties ) + { + sal_Int32 nIdx = rProp.mnIndex; + if( nIdx == -1 ) + continue; + OUString aPropName = rMapper->GetEntryAPIName( nIdx ); + if(rPropertyName == aPropName) + return rProp.maValue; + } + return aRet; +} + +void exportText( SvXMLExport& rExport, const OUString& rText, bool bConvertTabsLFs ) +{ + SvXMLElementExport aPara( rExport, XML_NAMESPACE_TEXT, + ::xmloff::token::GetXMLToken( ::xmloff::token::XML_P ), + true, false ); + + if( bConvertTabsLFs ) + { + sal_Int32 nStartPos = 0; + sal_Int32 nEndPos = rText.getLength(); + sal_Unicode cChar; + + for( sal_Int32 nPos = 0; nPos < nEndPos; nPos++ ) + { + cChar = rText[ nPos ]; + switch( cChar ) + { + case 0x0009: // tabulator + { + if( nPos > nStartPos ) + rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) ); + nStartPos = nPos + 1; + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_TEXT, + ::xmloff::token::GetXMLToken( ::xmloff::token::XML_TAB_STOP ), + false, false ); + } + break; + + case 0x000A: // linefeed + { + if( nPos > nStartPos ) + rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) ); + nStartPos = nPos + 1; + + SvXMLElementExport aElem( rExport, XML_NAMESPACE_TEXT, + ::xmloff::token::GetXMLToken( ::xmloff::token::XML_LINE_BREAK ), + false, false ); + } + break; + } + } + if( nEndPos > nStartPos ) + { + if( nStartPos == 0 ) + rExport.GetDocHandler()->characters( rText ); + else + rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nEndPos - nStartPos)) ); + } + } + else // do not convert tabs and linefeeds (eg for numbers coming from unit converter) + { + rExport.GetDocHandler()->characters( rText ); + } +} + +void exportRangeToSomewhere( SvXMLExport& rExport, const OUString& rValue ) +{ + //with issue #i366# and CWS chart20 ranges for error bars were introduced + //to keep them during copy paste from calc to impress for example it + //was necessary to introduce a mapping between the used ranges within calc and the data written to the local table + //this is why we write this ranges here + + //#i113950# first the range was exported to attribute text:id, but that attribute does not allow arbitrary strings anymore within ODF 1.2 + //as an alternative the range info is now saved into the description at an empty group element (not very nice, but ODF conform) + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( + rExport.getSaneDefaultVersion()); + if (nCurrentODFVersion == SvtSaveOptions::ODFSVER_010 || nCurrentODFVersion == SvtSaveOptions::ODFSVER_011) + return;//svg:desc is not allowed at draw:g in ODF1.0; but as the ranges for error bars are anyhow not allowed within ODF1.0 nor ODF1.1 we do not need the information + + SvXMLElementExport aEmptyShapeGroup( rExport, XML_NAMESPACE_DRAW, + ::xmloff::token::GetXMLToken( ::xmloff::token::XML_G ), + true, false ); + SvXMLElementExport aDescription( rExport, XML_NAMESPACE_SVG, + ::xmloff::token::GetXMLToken( ::xmloff::token::XML_DESC ), + true, false ); + rExport.GetDocHandler()->characters( rValue ); +} + +void setXMLRangePropertyAtDataSequence( + const Reference< chart2::data::XDataSequence > & xDataSequence, + const OUString & rXMLRange ) +{ + if( !xDataSequence.is()) + return; + try + { + static const OUStringLiteral aXMLRangePropName( u"CachedXMLRange" ); + Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW ); + Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo()); + if( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName )) + xProp->setPropertyValue( aXMLRangePropName, uno::Any( rXMLRange )); + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } +} + +bool getXMLRangePropertyFromDataSequence( + const Reference< chart2::data::XDataSequence > & xDataSequence, + OUString & rOutXMLRange, + bool bClearProp /* = false */) +{ + bool bResult = false; + if( xDataSequence.is()) + { + try + { + static const OUStringLiteral aXMLRangePropName( u"CachedXMLRange" ); + Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW ); + Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo()); + bResult = + ( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName ) && + ( xProp->getPropertyValue( aXMLRangePropName ) >>= rOutXMLRange ) && + !rOutXMLRange.isEmpty()); + // clear the property after usage + if( bClearProp && bResult ) + xProp->setPropertyValue( aXMLRangePropName, uno::Any( OUString())); + } + catch( const uno::Exception & ) + { + DBG_UNHANDLED_EXCEPTION("xmloff.chart"); + } + } + return bResult; +} + +void copyProperties( + const Reference< beans::XPropertySet > & xSource, + const Reference< beans::XPropertySet > & xDestination ) +{ + if( ! (xSource.is() && xDestination.is()) ) + return; + + try + { + Reference< beans::XPropertySetInfo > xSrcInfo( xSource->getPropertySetInfo(), uno::UNO_SET_THROW ); + Reference< beans::XPropertySetInfo > xDestInfo( xDestination->getPropertySetInfo(), uno::UNO_SET_THROW ); + const Sequence< beans::Property > aProperties( xSrcInfo->getProperties()); + for( const auto& rProperty : aProperties ) + { + OUString aName( rProperty.Name); + if( xDestInfo->hasPropertyByName( aName )) + { + beans::Property aProp( xDestInfo->getPropertyByName( aName )); + if( (aProp.Attributes & beans::PropertyAttribute::READONLY) == 0 ) + xDestination->setPropertyValue( + aName, xSource->getPropertyValue( aName )); + } + } + } + catch( const uno::Exception & ) + { + SAL_WARN("xmloff.chart", "Copying property sets failed!" ); + } +} + +bool switchBackToDataProviderFromParent( const Reference< chart2::XChartDocument >& xChartDoc, const tSchXMLLSequencesPerIndex & rLSequencesPerIndex ) +{ + //return whether the switch is successful + if( !xChartDoc.is() || !xChartDoc->hasInternalDataProvider() ) + return false; + Reference< chart2::data::XDataProvider > xDataProviderFromParent( SchXMLTools::getDataProviderFromParent( xChartDoc ) ); + if( !xDataProviderFromParent.is() ) + return false; + uno::Reference< chart2::data::XDataReceiver > xDataReceiver( xChartDoc, uno::UNO_QUERY ); + if( !xDataReceiver.is() ) + return false; + + xDataReceiver->attachDataProvider( xDataProviderFromParent ); + + for( const auto& rLSeq : rLSequencesPerIndex ) + { + Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( rLSeq.second ); + if( !xLabeledSeq.is() ) + continue; + Reference< chart2::data::XDataSequence > xNewSeq; + xNewSeq = lcl_createNewSequenceFromCachedXMLRange( xLabeledSeq->getValues(), xDataProviderFromParent ); + if( xNewSeq.is() ) + xLabeledSeq->setValues( xNewSeq ); + xNewSeq = lcl_createNewSequenceFromCachedXMLRange( xLabeledSeq->getLabel(), xDataProviderFromParent ); + if( xNewSeq.is() ) + xLabeledSeq->setLabel( xNewSeq ); + } + return true; +} + +void setBuildIDAtImportInfo( const uno::Reference< frame::XModel >& xModel, const Reference< beans::XPropertySet >& xImportInfo ) +{ + OUString aGenerator( lcl_getGeneratorFromModelOrItsParent(xModel) ); + if( !aGenerator.isEmpty() ) + SvXMLMetaDocumentContext::setBuildId( aGenerator, xImportInfo ); +} + +bool isDocumentGeneratedWithOpenOfficeOlderThan3_3( const uno::Reference< frame::XModel >& xChartModel ) +{ + bool bResult = isDocumentGeneratedWithOpenOfficeOlderThan3_0( xChartModel ); + if( !bResult ) + { + OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) ); + if( aGenerator.indexOf( "OpenOffice.org_project/3" ) != -1 ) + { + if( aGenerator.indexOf( "OpenOffice.org_project/300m" ) != -1 ) + { + sal_Int32 nBuilId = lcl_getBuildIDFromGenerator( lcl_getGeneratorFromModel(xChartModel) ); + if( nBuilId>0 && nBuilId<9491 ) //9491 is build id of dev300m76 + bResult= true; + } + else if( aGenerator.indexOf( "OpenOffice.org_project/310m" ) != -1 ) + bResult= true; + else if( aGenerator.indexOf( "OpenOffice.org_project/320m" ) != -1 ) + bResult= true; + } + } + return bResult; +} + +bool isDocumentGeneratedWithOpenOfficeOlderThan3_0( const uno::Reference< frame::XModel >& xChartModel ) +{ + bool bResult = isDocumentGeneratedWithOpenOfficeOlderThan2_3( xChartModel ); + if( !bResult ) + { + OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) ); + if( aGenerator.indexOf( "OpenOffice.org_project/680m" ) != -1 ) + bResult= true; + } + return bResult; +} + +bool isDocumentGeneratedWithOpenOfficeOlderThan2_4( const uno::Reference< frame::XModel >& xChartModel ) +{ + if( isDocumentGeneratedWithOpenOfficeOlderThan2_3( xChartModel ) ) + return true; + + if( isDocumentGeneratedWithOpenOfficeOlderThan3_0( xChartModel ) ) + { + sal_Int32 nBuilId = lcl_getBuildIDFromGenerator( lcl_getGeneratorFromModel(xChartModel) ); + if( nBuilId>0 && nBuilId<=9238 ) //9238 is build id of OpenOffice.org 2.3.1 + return true; + } + return false; +} + +bool isDocumentGeneratedWithOpenOfficeOlderThan2_3( const uno::Reference< frame::XModel >& xChartModel ) +{ + bool bResult = false; + OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) ); + //if there is a meta stream at the chart object it was not written with an older OpenOffice version < 2.3 + if( aGenerator.isEmpty() ) + { + //if there is no meta stream at the chart object we need to check whether the parent document is OpenOffice at all + uno::Reference< container::XChild > xChild( xChartModel, uno::UNO_QUERY ); + if( xChild.is() ) + { + aGenerator = lcl_getGeneratorFromModel( uno::Reference< frame::XModel >( xChild->getParent(), uno::UNO_QUERY) ); + if( aGenerator.indexOf( "OpenOffice.org_project" ) != -1 ) + { + //the chart application has not created files without a meta stream since OOo 2.3 (OOo 2.3 has written a metastream already) + //only the report builder extension has created some files with OOo 3.1 that do not have a meta stream + if( aGenerator.indexOf( "OpenOffice.org_project/31" ) != -1 ) + bResult = false;//#i100102# probably generated with OOo 3.1 by the report designer + else + bResult= true; //in this case the OLE chart was created by an older version, as OLE objects are sometimes stream copied the version can differ from the parents version, so the parents version is not a reliable indicator + } + else if( isDocumentGeneratedWithOpenOfficeOlderThan2_0(xChartModel) ) + bResult= true; + } + } + return bResult; +} + +bool isDocumentGeneratedWithOpenOfficeOlderThan2_0( const css::uno::Reference< css::frame::XModel >& xChartModel) +{ + bool bResult = false; + OUString aGenerator( lcl_getGeneratorFromModelOrItsParent(xChartModel) ); + if( aGenerator.startsWith( "OpenOffice.org 1" ) + || aGenerator.startsWith( "StarOffice 6" ) + || aGenerator.startsWith( "StarOffice 7" ) + || aGenerator.startsWith( "StarSuite 6" ) + || aGenerator.startsWith( "StarSuite 7" ) + ) + bResult= true; + return bResult; +} + +Reference< chart2::data::XDataProvider > getDataProviderFromParent( const Reference< chart2::XChartDocument >& xChartDoc ) +{ + Reference< chart2::data::XDataProvider > xRet; + uno::Reference< container::XChild > xChild( xChartDoc, uno::UNO_QUERY ); + if( xChild.is() ) + { + Reference< lang::XMultiServiceFactory > xFact( xChild->getParent(), uno::UNO_QUERY ); + if( xFact.is() ) + { + static const OUStringLiteral aDataProviderServiceName( u"com.sun.star.chart2.data.DataProvider"); + const uno::Sequence< OUString > aServiceNames( xFact->getAvailableServiceNames()); + const OUString * pBegin = aServiceNames.getConstArray(); + const OUString * pEnd = pBegin + aServiceNames.getLength(); + if( ::std::find( pBegin, pEnd, aDataProviderServiceName ) != pEnd ) + { + xRet.set( xFact->createInstance( aDataProviderServiceName ), uno::UNO_QUERY ); + } + } + } + return xRet; +} + +} // namespace SchXMLTools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/SchXMLTools.hxx b/xmloff/source/chart/SchXMLTools.hxx new file mode 100644 index 000000000..885e57aef --- /dev/null +++ b/xmloff/source/chart/SchXMLTools.hxx @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <rtl/ustring.hxx> +#include <xmloff/xmltoken.hxx> +#include "transporttypes.hxx" + +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/chart2/data/XLabeledDataSequence2.hpp> + +namespace com::sun::star { + namespace chart2 { + class XChartDocument; + class XRegressionCurve; + namespace data { + class XDataProvider; + } + } +} + +class XMLPropStyleContext; +class SvXMLStylesContext; +class SvXMLExport; + +namespace SchXMLTools +{ + bool isDocumentGeneratedWithOpenOfficeOlderThan2_0( const css::uno::Reference< css::frame::XModel >& xChartModel); + bool isDocumentGeneratedWithOpenOfficeOlderThan2_3( const css::uno::Reference< css::frame::XModel >& xChartModel); + bool isDocumentGeneratedWithOpenOfficeOlderThan2_4( const css::uno::Reference< css::frame::XModel >& xChartModel); + bool isDocumentGeneratedWithOpenOfficeOlderThan3_0( const css::uno::Reference< css::frame::XModel >& xChartModel); + bool isDocumentGeneratedWithOpenOfficeOlderThan3_3( const css::uno::Reference< css::frame::XModel >& xChartModel); + + void setBuildIDAtImportInfo( const css::uno::Reference< css::frame::XModel >& xModel + , const css::uno::Reference< css::beans::XPropertySet >& xImportInfo ); + + enum SchXMLChartTypeEnum + { + XML_CHART_CLASS_LINE, + XML_CHART_CLASS_AREA, + XML_CHART_CLASS_CIRCLE, + XML_CHART_CLASS_RING, + XML_CHART_CLASS_SCATTER, + XML_CHART_CLASS_RADAR, + XML_CHART_CLASS_FILLED_RADAR, + XML_CHART_CLASS_BAR, + XML_CHART_CLASS_STOCK, + XML_CHART_CLASS_BUBBLE, + XML_CHART_CLASS_ADDIN, + XML_CHART_CLASS_UNKNOWN + }; + + SchXMLChartTypeEnum GetChartTypeEnum( std::u16string_view rClassName ); + + OUString GetChartTypeByClassName( + std::u16string_view rClassName, bool bUseOldNames ); + + ::xmloff::token::XMLTokenEnum getTokenByChartType( + const OUString & rChartTypeService, bool bUseOldNames ); + + OUString GetNewChartTypeName( const OUString & rOldChartTypeName ); + + css::uno::Reference< + css::chart2::data::XLabeledDataSequence2 > GetNewLabeledDataSequence(); + + css::uno::Reference< css::chart2::data::XDataSequence > CreateDataSequence( + const OUString& rRange, + const css::uno::Reference< css::chart2::XChartDocument >& xChartDoc ); + + css::uno::Reference< css::chart2::data::XDataSequence > CreateDataSequenceWithoutConvert( + const OUString& rRange, + const css::uno::Reference< css::chart2::XChartDocument >& xChartDoc ); + + void CreateCategories( + const css::uno::Reference< css::chart2::data::XDataProvider > & xDataProvider, + const css::uno::Reference< css::chart2::XChartDocument > & xNewDoc, + const OUString & rRangeAddress, + sal_Int32 nCooSysIndex, + sal_Int32 nDimensionIndex, + tSchXMLLSequencesPerIndex * pLSequencesPerIndex = nullptr ); + + css::uno::Any getPropertyFromContext( std::u16string_view rPropertyName, const XMLPropStyleContext * pPropStyleContext, const SvXMLStylesContext* pStylesCtxt ); + + void exportText( SvXMLExport& rExport, const OUString& rText, bool bConvertTabsLFs ); + + void exportRangeToSomewhere( SvXMLExport& rExport, const OUString& rValue ); + + /** checks if the data sequence has the property "CachedXMLRange" (true for + internal data sequences), and if so sets this property to the range + given in rXMLRange + */ + void setXMLRangePropertyAtDataSequence( + const css::uno::Reference< css::chart2::data::XDataSequence > & xDataSequence, + const OUString & rXMLRange ); + + /** checks if the data sequence has the property "CachedXMLRange" (true for + internal data sequences), and if so retrieves this property and applies + it to the range given in rOutXMLRange. + + @param bClearProp If true, the property is reset to its default after it + was assigned to rOutXMLRange + + @return true, if the property was found, assigned and is non-empty + */ + bool getXMLRangePropertyFromDataSequence( + const css::uno::Reference< css::chart2::data::XDataSequence > & xDataSequence, + OUString & rOutXMLRange, + bool bClearProp ); + + css::uno::Reference< css::chart2::data::XDataProvider > getDataProviderFromParent( const css::uno::Reference< css::chart2::XChartDocument >& xChartDoc ); + + bool switchBackToDataProviderFromParent( const css::uno::Reference< css::chart2::XChartDocument >& xChartDoc + , const tSchXMLLSequencesPerIndex & rLSequencesPerIndex ); + + void copyProperties( + const css::uno::Reference< css::beans::XPropertySet > & xSource, + const css::uno::Reference< css::beans::XPropertySet > & xDestination ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLAxisPositionPropertyHdl.cxx b/xmloff/source/chart/XMLAxisPositionPropertyHdl.cxx new file mode 100644 index 000000000..6508031ba --- /dev/null +++ b/xmloff/source/chart/XMLAxisPositionPropertyHdl.cxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "XMLAxisPositionPropertyHdl.hxx" + +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/chart/ChartAxisPosition.hpp> + +#include <sax/tools/converter.hxx> + +#include <xmloff/xmltoken.hxx> + + +using namespace ::xmloff::token; + +using namespace com::sun::star; + +XMLAxisPositionPropertyHdl::XMLAxisPositionPropertyHdl( bool bCrossingValue ) + : m_bCrossingValue( bCrossingValue ) +{} + +XMLAxisPositionPropertyHdl::~XMLAxisPositionPropertyHdl() +{} + +bool XMLAxisPositionPropertyHdl::importXML( const OUString& rStrImpValue, + uno::Any& rValue, const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + bool bResult = false; + + if( rStrImpValue == GetXMLToken(XML_START) ) + { + if( !m_bCrossingValue ) + { + rValue <<= css::chart::ChartAxisPosition_START; + bResult = true; + } + } + else if( rStrImpValue == GetXMLToken(XML_END) ) + { + if( !m_bCrossingValue ) + { + rValue <<= css::chart::ChartAxisPosition_END; + bResult = true; + } + } + else if( rStrImpValue == GetXMLToken(XML_0) ) + { + if( !m_bCrossingValue ) + { + rValue <<= css::chart::ChartAxisPosition_ZERO; + bResult = true; + } + } + else + { + if( !m_bCrossingValue ) + { + rValue <<= css::chart::ChartAxisPosition_VALUE; + bResult = true; + } + else + { + double fDblValue=0.0; + bResult = ::sax::Converter::convertDouble(fDblValue, rStrImpValue); + rValue <<= fDblValue; + } + } + + return bResult; +} + +bool XMLAxisPositionPropertyHdl::exportXML( OUString& rStrExpValue, + const uno::Any& rValue, const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + bool bResult = false; + + OUStringBuffer sValueBuffer; + if( m_bCrossingValue ) + { + if(rStrExpValue.isEmpty()) + { + double fValue = 0.0; + rValue >>= fValue; + ::sax::Converter::convertDouble( sValueBuffer, fValue ); + rStrExpValue = sValueBuffer.makeStringAndClear(); + bResult = true; + } + } + else + { + css::chart::ChartAxisPosition ePosition( css::chart::ChartAxisPosition_ZERO ); + rValue >>= ePosition; + switch(ePosition) + { + case css::chart::ChartAxisPosition_START: + rStrExpValue = GetXMLToken( XML_START ); + bResult = true; + break; + case css::chart::ChartAxisPosition_END: + rStrExpValue = GetXMLToken( XML_END ); + bResult = true; + break; + case css::chart::ChartAxisPosition_ZERO: + ::sax::Converter::convertDouble( sValueBuffer, 0.0 ); + rStrExpValue = sValueBuffer.makeStringAndClear(); + bResult = true; + break; + default: + break; + } + } + return bResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLAxisPositionPropertyHdl.hxx b/xmloff/source/chart/XMLAxisPositionPropertyHdl.hxx new file mode 100644 index 000000000..dd779c11d --- /dev/null +++ b/xmloff/source/chart/XMLAxisPositionPropertyHdl.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlprhdl.hxx> + +class XMLAxisPositionPropertyHdl : public XMLPropertyHandler +{ +public: + explicit XMLAxisPositionPropertyHdl( bool bCrossingValue ); + virtual ~XMLAxisPositionPropertyHdl() override; + + virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override; + virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override; + +private: + bool m_bCrossingValue; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLChartPropertyContext.cxx b/xmloff/source/chart/XMLChartPropertyContext.cxx new file mode 100644 index 000000000..a06cad478 --- /dev/null +++ b/xmloff/source/chart/XMLChartPropertyContext.cxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XMLChartPropertyContext.hxx" +#include "PropertyMap.hxx" + +#include "XMLSymbolImageContext.hxx" +#include "XMLLabelSeparatorContext.hxx" +#include <xmloff/xmlimppr.hxx> +#include <xmloff/xmlprmap.hxx> + +using namespace ::com::sun::star; + +XMLChartPropertyContext::XMLChartPropertyContext( + SvXMLImport& rImport, + sal_Int32 nElement, + const uno::Reference< xml::sax::XFastAttributeList > & xAttrList, + sal_uInt32 nFamily, + ::std::vector< XMLPropertyState >& rProps, + const rtl::Reference< SvXMLImportPropertyMapper >& rMapper ) : + SvXMLPropertySetContext( rImport, nElement, xAttrList, nFamily, rProps, rMapper ) +{ +} + +XMLChartPropertyContext::~XMLChartPropertyContext() +{} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLChartPropertyContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList, + ::std::vector< XMLPropertyState > &rProperties, + const XMLPropertyState& rProp ) +{ + switch( mxMapper->getPropertySetMapper()->GetEntryContextId( rProp.mnIndex ) ) + { + case XML_SCH_CONTEXT_SPECIAL_SYMBOL_IMAGE: + return new XMLSymbolImageContext( GetImport(), nElement, rProp, rProperties ); + case XML_SCH_CONTEXT_SPECIAL_LABEL_SEPARATOR: + return new XMLLabelSeparatorContext( GetImport(), nElement, rProp, rProperties ); + } + + // default / no context yet: create child context by base class + return SvXMLPropertySetContext::createFastChildContext( + nElement, xAttrList, rProperties, rProp ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLChartPropertyContext.hxx b/xmloff/source/chart/XMLChartPropertyContext.hxx new file mode 100644 index 000000000..d0fa489d3 --- /dev/null +++ b/xmloff/source/chart/XMLChartPropertyContext.hxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlprcon.hxx> + +class XMLChartPropertyContext : public SvXMLPropertySetContext +{ +public: + + XMLChartPropertyContext( SvXMLImport& rImport, sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList, + sal_uInt32 nFamily, + ::std::vector< XMLPropertyState >& rProps, + const rtl::Reference< SvXMLImportPropertyMapper >& rMapper ); + virtual ~XMLChartPropertyContext() override; + + using SvXMLPropertySetContext::createFastChildContext; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList, + ::std::vector< XMLPropertyState > &rProperties, + const XMLPropertyState& rProp ) override; + +private: +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLChartStyleContext.cxx b/xmloff/source/chart/XMLChartStyleContext.cxx new file mode 100644 index 000000000..b17bf5d82 --- /dev/null +++ b/xmloff/source/chart/XMLChartStyleContext.cxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <XMLChartStyleContext.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlnumfi.hxx> +#include <xmloff/families.hxx> +#include <xmloff/xmltypes.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmlimppr.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <tools/diagnose_ex.h> + +#include "XMLChartPropertyContext.hxx" + +using namespace com::sun::star; +using ::xmloff::token::IsXMLToken; +using ::xmloff::token::XML_DATA_STYLE_NAME; +using ::xmloff::token::XML_PERCENTAGE_DATA_STYLE_NAME; +using ::xmloff::token::XML_TEXT_PROPERTIES; +using ::xmloff::token::XML_PARAGRAPH_PROPERTIES; +using ::xmloff::token::XML_GRAPHIC_PROPERTIES; +using ::xmloff::token::XML_CHART_PROPERTIES; + + +void XMLChartStyleContext::SetAttribute( + sal_Int32 nElement, + const OUString& rValue ) +{ + switch (nElement & TOKEN_MASK) + { + case XML_DATA_STYLE_NAME: + msDataStyleName = rValue; + break; + case XML_PERCENTAGE_DATA_STYLE_NAME: + msPercentageDataStyleName = rValue; + break; + default: + XMLShapeStyleContext::SetAttribute( nElement, rValue ); + } +} + +XMLChartStyleContext::XMLChartStyleContext( + SvXMLImport& rImport, + SvXMLStylesContext& rStyles, XmlStyleFamily nFamily ) : + XMLShapeStyleContext( rImport, rStyles, nFamily ), + mrStyles( rStyles ) +{} + +XMLChartStyleContext::~XMLChartStyleContext() +{} + +namespace +{ + +void lcl_NumberFormatStyleToProperty( const OUString& rStyleName, const OUString& rPropertyName, + const SvXMLStylesContext& rStylesContext, + const uno::Reference< beans::XPropertySet >& rPropSet ) +{ + if( !rStyleName.isEmpty()) + { + const SvXMLNumFormatContext* pStyle = static_cast<const SvXMLNumFormatContext *>(rStylesContext.FindStyleChildContext( + XmlStyleFamily::DATA_STYLE, rStyleName, true )); + if( pStyle ) + { + sal_Int32 nNumberFormat = const_cast<SvXMLNumFormatContext*>(pStyle)->GetKey(); + rPropSet->setPropertyValue( rPropertyName, uno::Any(nNumberFormat) ); + } + } +} + +} + +void XMLChartStyleContext::FillPropertySet( + const uno::Reference< beans::XPropertySet > & rPropSet ) +{ + try + { + XMLShapeStyleContext::FillPropertySet( rPropSet ); + } + catch( beans::UnknownPropertyException& ) + { + TOOLS_WARN_EXCEPTION( "xmloff", "unknown property exception -> shape style not completely imported for chart style" ); + } + + lcl_NumberFormatStyleToProperty( msDataStyleName, "NumberFormat", mrStyles, rPropSet ); + lcl_NumberFormatStyleToProperty( msPercentageDataStyleName, "PercentageNumberFormat", mrStyles, rPropSet ); +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLChartStyleContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + if( IsTokenInNamespace(nElement, XML_NAMESPACE_STYLE) || + IsTokenInNamespace(nElement, XML_NAMESPACE_LO_EXT) ) + { + sal_Int32 nLocalName = nElement & TOKEN_MASK; + sal_uInt32 nFamily = 0; + if( nLocalName == XML_TEXT_PROPERTIES ) + nFamily = XML_TYPE_PROP_TEXT; + else if( nLocalName == XML_PARAGRAPH_PROPERTIES ) + nFamily = XML_TYPE_PROP_PARAGRAPH; + else if( nLocalName == XML_GRAPHIC_PROPERTIES ) + nFamily = XML_TYPE_PROP_GRAPHIC; + else if( nLocalName == XML_CHART_PROPERTIES ) + nFamily = XML_TYPE_PROP_CHART; + if( nFamily ) + { + rtl::Reference < SvXMLImportPropertyMapper > xImpPrMap = + GetStyles()->GetImportPropertyMapper( GetFamily() ); + if( xImpPrMap.is() ) + return new XMLChartPropertyContext( + GetImport(), nElement, xAttrList, nFamily, + GetProperties(), xImpPrMap ); + } + } + + return XMLShapeStyleContext::createFastChildContext( nElement, xAttrList ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLErrorBarStylePropertyHdl.cxx b/xmloff/source/chart/XMLErrorBarStylePropertyHdl.cxx new file mode 100644 index 000000000..c50450740 --- /dev/null +++ b/xmloff/source/chart/XMLErrorBarStylePropertyHdl.cxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XMLErrorBarStylePropertyHdl.hxx" + +#include <xmloff/xmluconv.hxx> +#include <unotools/saveopt.hxx> + +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/uno/Any.hxx> + +using namespace com::sun::star; + +XMLErrorBarStylePropertyHdl::XMLErrorBarStylePropertyHdl( const SvXMLEnumMapEntry<sal_Int32>* pEnumMap ) + : XMLEnumPropertyHdl( pEnumMap ) +{ +} + +XMLErrorBarStylePropertyHdl::~XMLErrorBarStylePropertyHdl() +{ +} + +bool XMLErrorBarStylePropertyHdl::exportXML( OUString& rStrExpValue, + const uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter) const +{ + uno::Any aValue(rValue); + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(rUnitConverter.getSaneDefaultVersion()); + if (nCurrentVersion < SvtSaveOptions::ODFSVER_012) + { + sal_Int32 nValue = 0; + if(rValue >>= nValue ) + { + if( nValue == css::chart::ErrorBarStyle::STANDARD_ERROR + || nValue == css::chart::ErrorBarStyle::FROM_DATA ) + { + nValue = css::chart::ErrorBarStyle::NONE; + aValue <<= nValue; + } + } + } + + return XMLEnumPropertyHdl::exportXML( rStrExpValue, aValue, rUnitConverter ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLErrorBarStylePropertyHdl.hxx b/xmloff/source/chart/XMLErrorBarStylePropertyHdl.hxx new file mode 100644 index 000000000..2a006f0e3 --- /dev/null +++ b/xmloff/source/chart/XMLErrorBarStylePropertyHdl.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/EnumPropertyHdl.hxx> + +class XMLErrorBarStylePropertyHdl : public XMLEnumPropertyHdl +{ +public: + XMLErrorBarStylePropertyHdl(const SvXMLEnumMapEntry<sal_Int32>* pEnumMap); + virtual ~XMLErrorBarStylePropertyHdl() override; + + virtual bool exportXML(OUString& rStrExpValue, const css::uno::Any& rValue, + const SvXMLUnitConverter& rUnitConverter) const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLErrorIndicatorPropertyHdl.cxx b/xmloff/source/chart/XMLErrorIndicatorPropertyHdl.cxx new file mode 100644 index 000000000..65f39a9b6 --- /dev/null +++ b/xmloff/source/chart/XMLErrorIndicatorPropertyHdl.cxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "XMLErrorIndicatorPropertyHdl.hxx" + +#include <rtl/ustrbuf.hxx> + +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/chart/ChartErrorIndicatorType.hpp> + +#include <sax/tools/converter.hxx> + + +using namespace com::sun::star; + +XMLErrorIndicatorPropertyHdl::~XMLErrorIndicatorPropertyHdl() +{} + +bool XMLErrorIndicatorPropertyHdl::importXML( const OUString& rStrImpValue, + uno::Any& rValue, const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + bool bValue(false); + (void)::sax::Converter::convertBool( bValue, rStrImpValue ); + + // modify existing value + chart::ChartErrorIndicatorType eType = chart::ChartErrorIndicatorType_NONE; + if( rValue.hasValue()) + rValue >>= eType; + + if( bValue ) // enable flag + { + if( eType != chart::ChartErrorIndicatorType_TOP_AND_BOTTOM ) + { + if( mbUpperIndicator ) + eType = ( eType == chart::ChartErrorIndicatorType_LOWER ) + ? chart::ChartErrorIndicatorType_TOP_AND_BOTTOM + : chart::ChartErrorIndicatorType_UPPER; + else + eType = ( eType == chart::ChartErrorIndicatorType_UPPER ) + ? chart::ChartErrorIndicatorType_TOP_AND_BOTTOM + : chart::ChartErrorIndicatorType_LOWER; + } + } + else // disable flag + { + if( eType != chart::ChartErrorIndicatorType_NONE ) + { + if( mbUpperIndicator ) + eType = ( eType == chart::ChartErrorIndicatorType_UPPER ) + ? chart::ChartErrorIndicatorType_NONE + : chart::ChartErrorIndicatorType_LOWER; + else + eType = ( eType == chart::ChartErrorIndicatorType_LOWER ) + ? chart::ChartErrorIndicatorType_NONE + : chart::ChartErrorIndicatorType_UPPER; + } + } + + rValue <<= eType; + + return true; +} + +bool XMLErrorIndicatorPropertyHdl::exportXML( OUString& rStrExpValue, + const uno::Any& rValue, const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + chart::ChartErrorIndicatorType eType; + + rValue >>= eType; + bool bValue = ( eType == chart::ChartErrorIndicatorType_TOP_AND_BOTTOM || + ( mbUpperIndicator + ? ( eType == chart::ChartErrorIndicatorType_UPPER ) + : ( eType == chart::ChartErrorIndicatorType_LOWER ))); + + if( bValue ) + { + OUStringBuffer aBuffer; + ::sax::Converter::convertBool( aBuffer, bValue ); + rStrExpValue = aBuffer.makeStringAndClear(); + } + + // only export if set to true + return bValue; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLErrorIndicatorPropertyHdl.hxx b/xmloff/source/chart/XMLErrorIndicatorPropertyHdl.hxx new file mode 100644 index 000000000..d33e66413 --- /dev/null +++ b/xmloff/source/chart/XMLErrorIndicatorPropertyHdl.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlprhdl.hxx> + +class XMLErrorIndicatorPropertyHdl : public XMLPropertyHandler +{ +private: + bool mbUpperIndicator; + +public: + explicit XMLErrorIndicatorPropertyHdl( bool bUpper ) : mbUpperIndicator( bUpper ) + {} + virtual ~XMLErrorIndicatorPropertyHdl() override; + + virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override; + virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLLabelSeparatorContext.cxx b/xmloff/source/chart/XMLLabelSeparatorContext.cxx new file mode 100644 index 000000000..93beff46c --- /dev/null +++ b/xmloff/source/chart/XMLLabelSeparatorContext.cxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XMLLabelSeparatorContext.hxx" + +#include "SchXMLParagraphContext.hxx" +#include <sal/log.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlimp.hxx> + + +using namespace ::com::sun::star; + +XMLLabelSeparatorContext::XMLLabelSeparatorContext( + SvXMLImport& rImport, sal_Int32 nElement, + const XMLPropertyState& rProp, + ::std::vector< XMLPropertyState > &rProps ) : + XMLElementPropertyContext( + rImport, nElement, rProp, rProps ) +{ +} + +XMLLabelSeparatorContext::~XMLLabelSeparatorContext() +{} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLLabelSeparatorContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + if( (nElement & TOKEN_MASK) == xmloff::token::XML_P ) + { + return new SchXMLParagraphContext( GetImport(), m_aSeparator ); + } + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return nullptr; +} + +void XMLLabelSeparatorContext::endFastElement(sal_Int32 nElement) +{ + if( !m_aSeparator.isEmpty() ) + { + // aProp is a member of XMLElementPropertyContext + aProp.maValue <<= m_aSeparator; + SetInsert( true ); + } + + XMLElementPropertyContext::endFastElement(nElement); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLLabelSeparatorContext.hxx b/xmloff/source/chart/XMLLabelSeparatorContext.hxx new file mode 100644 index 000000000..83e69f01a --- /dev/null +++ b/xmloff/source/chart/XMLLabelSeparatorContext.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <XMLElementPropertyContext.hxx> + +class XMLLabelSeparatorContext : public XMLElementPropertyContext +{ +public: + XMLLabelSeparatorContext(SvXMLImport& rImport, sal_Int32 nElement, + const XMLPropertyState& rProp, + ::std::vector<XMLPropertyState>& rProps); + virtual ~XMLLabelSeparatorContext() override; + + virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference<css::xml::sax::XFastAttributeList>& AttrList) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + +private: + OUString m_aSeparator; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLSymbolImageContext.cxx b/xmloff/source/chart/XMLSymbolImageContext.cxx new file mode 100644 index 000000000..646f1b8c0 --- /dev/null +++ b/xmloff/source/chart/XMLSymbolImageContext.cxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XMLSymbolImageContext.hxx" +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmltkmap.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlimp.hxx> +#include <xmloff/namespacemap.hxx> +#include <xmloff/XMLBase64ImportContext.hxx> +#include <com/sun/star/io/XOutputStream.hpp> +#include <com/sun/star/graphic/XGraphic.hpp> +#include <sal/log.hxx> + +using namespace css; +using namespace xmloff::token; + +XMLSymbolImageContext::XMLSymbolImageContext( + SvXMLImport& rImport, sal_Int32 nElement, + const XMLPropertyState& rProp, + ::std::vector< XMLPropertyState > &rProps ) : + XMLElementPropertyContext( + rImport, nElement, rProp, rProps ) +{ +} + +XMLSymbolImageContext::~XMLSymbolImageContext() +{} + +void XMLSymbolImageContext::startFastElement( + sal_Int32 /*nElement*/, + const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) + { + const OUString sValue = aIter.toString(); + + switch( aIter.getToken() ) + { + case XML_ELEMENT(XLINK, XML_HREF): + msURL = sValue; + break; + case XML_ELEMENT(XLINK, XML_ACTUATE): + case XML_ELEMENT(XLINK, XML_TYPE): + case XML_ELEMENT(XLINK, XML_SHOW): + // these values are currently not interpreted + // it is always assumed 'actuate=onLoad', 'type=simple', 'show=embed' + break; + default: + XMLOFF_WARN_UNKNOWN("xmloff", aIter); + } + } +} + +css::uno::Reference< css::xml::sax::XFastContextHandler > XMLSymbolImageContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + if( (nElement & TOKEN_MASK) == xmloff::token::XML_BINARY_DATA ) + { + if( msURL.isEmpty() && ! mxBase64Stream.is() ) + { + mxBase64Stream = GetImport().GetStreamForGraphicObjectURLFromBase64(); + if( mxBase64Stream.is() ) + return new XMLBase64ImportContext( GetImport(), mxBase64Stream ); + } + } + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + return nullptr; +} + +void XMLSymbolImageContext::endFastElement(sal_Int32 nElement) +{ + uno::Reference<graphic::XGraphic> xGraphic; + + if (!msURL.isEmpty()) + { + xGraphic = GetImport().loadGraphicByURL(msURL); + } + else if (mxBase64Stream.is()) + { + xGraphic = GetImport().loadGraphicFromBase64(mxBase64Stream); + mxBase64Stream = nullptr; + } + + if (xGraphic.is()) + { + // aProp is a member of XMLElementPropertyContext + aProp.maValue <<= xGraphic; + SetInsert( true ); + } + + XMLElementPropertyContext::endFastElement(nElement); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLSymbolImageContext.hxx b/xmloff/source/chart/XMLSymbolImageContext.hxx new file mode 100644 index 000000000..a4796775c --- /dev/null +++ b/xmloff/source/chart/XMLSymbolImageContext.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <XMLElementPropertyContext.hxx> + +namespace com::sun::star { + namespace io { class XOutputStream; } +} + +class XMLSymbolImageContext : public XMLElementPropertyContext +{ +public: + + XMLSymbolImageContext( SvXMLImport& rImport, sal_Int32 nElement, + const XMLPropertyState& rProp, + ::std::vector< XMLPropertyState > &rProps ); + virtual ~XMLSymbolImageContext() override; + + virtual void SAL_CALL startFastElement( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; + virtual void SAL_CALL endFastElement(sal_Int32 nElement) override; + +private: + OUString msURL; + css::uno::Reference < css::io::XOutputStream > mxBase64Stream; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLSymbolTypePropertyHdl.cxx b/xmloff/source/chart/XMLSymbolTypePropertyHdl.cxx new file mode 100644 index 000000000..dd24c1340 --- /dev/null +++ b/xmloff/source/chart/XMLSymbolTypePropertyHdl.cxx @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XMLSymbolTypePropertyHdl.hxx" +#include <xmloff/xmluconv.hxx> +#include <rtl/ustrbuf.hxx> + +using namespace ::xmloff::token; + +namespace +{ +struct SvXMLSignedEnumMapEntry +{ + ::xmloff::token::XMLTokenEnum eToken; + sal_Int32 nValue; +}; + +const SvXMLSignedEnumMapEntry aXMLChartSymbolTypeEnumMap[] = +{ + { XML_NONE, -3 }, + { XML_AUTOMATIC, -2 }, + { XML_IMAGE, -1 }, + { XML_TOKEN_INVALID, 0 } +}; + +const SvXMLSignedEnumMapEntry aXMLChartSymbolNameMap[] = +{ + { XML_GRADIENTSTYLE_SQUARE, 0 }, // "square" + { XML_DIAMOND, 1 }, + { XML_ARROW_DOWN, 2 }, + { XML_ARROW_UP, 3 }, + { XML_ARROW_RIGHT, 4 }, + { XML_ARROW_LEFT, 5 }, + { XML_BOW_TIE, 6 }, + { XML_HOURGLASS, 7 }, + { XML_CIRCLE, 8 }, + { XML_STAR, 9 }, + { XML_X, 10 }, + { XML_PLUS, 11 }, + { XML_ASTERISK, 12 }, + { XML_HORIZONTAL_BAR, 13 }, + { XML_VERTICAL_BAR, 14 }, + { XML_TOKEN_INVALID, 0 } +}; + +bool lcl_convertEnum( + OUStringBuffer & rBuffer, + sal_Int32 nValue, + const SvXMLSignedEnumMapEntry *pMap ) +{ + enum XMLTokenEnum eTok = XML_TOKEN_INVALID; + + while( pMap->eToken != XML_TOKEN_INVALID ) + { + if( pMap->nValue == nValue ) + { + eTok = pMap->eToken; + break; + } + pMap++; + } + + if( eTok != XML_TOKEN_INVALID ) + rBuffer.append( GetXMLToken(eTok) ); + + return (eTok != XML_TOKEN_INVALID); +} + +bool lcl_convertEnum( + sal_Int32 & rEnum, + std::u16string_view rValue, + const SvXMLSignedEnumMapEntry *pMap ) +{ + while( pMap->eToken != XML_TOKEN_INVALID ) + { + if( IsXMLToken( rValue, pMap->eToken ) ) + { + rEnum = pMap->nValue; + return true; + } + pMap++; + } + return false; +} + +} // anonymous namespace + +using namespace com::sun::star; + +XMLSymbolTypePropertyHdl::XMLSymbolTypePropertyHdl( bool bIsNamedSymbol ) + : m_bIsNamedSymbol( bIsNamedSymbol ) +{} + +XMLSymbolTypePropertyHdl::~XMLSymbolTypePropertyHdl() +{} + +bool XMLSymbolTypePropertyHdl::importXML( const OUString& rStrImpValue, + uno::Any& rValue, const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + bool bResult = false; + + if( m_bIsNamedSymbol ) + { + sal_Int32 nValue = -3; // NONE + bResult = lcl_convertEnum( nValue, rStrImpValue, aXMLChartSymbolNameMap ); + rValue <<= nValue; + } + else + { + sal_Int32 nValue = -3; // NONE + bResult = lcl_convertEnum( nValue, rStrImpValue, aXMLChartSymbolTypeEnumMap ); + rValue <<= nValue; + } + + return bResult; +} + +bool XMLSymbolTypePropertyHdl::exportXML( OUString& rStrExpValue, + const uno::Any& rValue, const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + bool bResult = false; + + sal_Int32 nType = -3; // NONE + rValue >>= nType; + + if( m_bIsNamedSymbol ) + { + OUStringBuffer aBuf; + bResult = lcl_convertEnum( aBuf, nType, aXMLChartSymbolNameMap ); + rStrExpValue = aBuf.makeStringAndClear(); + } + else + { + if( nType < 0 ) + { + OUStringBuffer aBuf; + bResult = lcl_convertEnum( aBuf, nType, aXMLChartSymbolTypeEnumMap ); + rStrExpValue = aBuf.makeStringAndClear(); + } + else + { + bResult = true; + rStrExpValue = GetXMLToken( XML_NAMED_SYMBOL ); + } + } + + return bResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLSymbolTypePropertyHdl.hxx b/xmloff/source/chart/XMLSymbolTypePropertyHdl.hxx new file mode 100644 index 000000000..78d2be686 --- /dev/null +++ b/xmloff/source/chart/XMLSymbolTypePropertyHdl.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlprhdl.hxx> + +class XMLSymbolTypePropertyHdl : public XMLPropertyHandler +{ +public: + explicit XMLSymbolTypePropertyHdl( bool bIsNamedSymbol ); + virtual ~XMLSymbolTypePropertyHdl() override; + + virtual bool importXML( const OUString& rStrImpValue, css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override; + virtual bool exportXML( OUString& rStrExpValue, const css::uno::Any& rValue, const SvXMLUnitConverter& rUnitConverter ) const override; + +private: + bool m_bIsNamedSymbol; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLTextOrientationHdl.cxx b/xmloff/source/chart/XMLTextOrientationHdl.cxx new file mode 100644 index 000000000..7f23e58c1 --- /dev/null +++ b/xmloff/source/chart/XMLTextOrientationHdl.cxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "XMLTextOrientationHdl.hxx" +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmltoken.hxx> + +using namespace ::com::sun::star; +using namespace ::xmloff::token; + +XMLTextOrientationHdl::~XMLTextOrientationHdl() +{ +} + +bool XMLTextOrientationHdl::importXML( + const OUString& rStrImpValue, + css::uno::Any& rValue, + const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + bool bRetval( false ); + + if( IsXMLToken( rStrImpValue, XML_LTR )) + { + rValue <<= false; + bRetval = true; + } + else if( IsXMLToken( rStrImpValue, XML_TTB )) + { + rValue <<= true; + bRetval = true; + } + + return bRetval; +} + +bool XMLTextOrientationHdl::exportXML( + OUString& rStrExpValue, + const css::uno::Any& rValue, + const SvXMLUnitConverter& /*rUnitConverter*/ ) const +{ + bool bVal (false ); + bool bRetval( false ); + + if( rValue >>= bVal ) + { + if( bVal ) + rStrExpValue = GetXMLToken( XML_TTB ); + else + rStrExpValue = GetXMLToken( XML_LTR ); + bRetval = true; + } + + return bRetval; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/XMLTextOrientationHdl.hxx b/xmloff/source/chart/XMLTextOrientationHdl.hxx new file mode 100644 index 000000000..e84dcb909 --- /dev/null +++ b/xmloff/source/chart/XMLTextOrientationHdl.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <xmloff/xmlprhdl.hxx> + +class XMLTextOrientationHdl : public XMLPropertyHandler +{ +private: +public: + virtual ~XMLTextOrientationHdl() override; + + virtual bool importXML( const OUString& rStrImpValue, + css::uno::Any& rValue, + const SvXMLUnitConverter& rUnitConverter ) const override; + virtual bool exportXML( OUString& rStrExpValue, + const css::uno::Any& rValue, + const SvXMLUnitConverter& rUnitConverter ) const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/contexts.cxx b/xmloff/source/chart/contexts.cxx new file mode 100644 index 000000000..b11790df7 --- /dev/null +++ b/xmloff/source/chart/contexts.cxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmlmetai.hxx> +#include <xmloff/xmlstyle.hxx> +#include <SchXMLImport.hxx> +#include "SchXMLCalculationSettingsContext.hxx" + +#include "contexts.hxx" + +#include <sal/log.hxx> + +using namespace com::sun::star; +using namespace ::xmloff::token; + +namespace { + +class SchXMLBodyContext_Impl : public SvXMLImportContext +{ +private: + SchXMLImportHelper& mrImportHelper; + +public: + + SchXMLBodyContext_Impl( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport ); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +} + +SchXMLBodyContext_Impl::SchXMLBodyContext_Impl( + SchXMLImportHelper& rImpHelper, SvXMLImport& rImport ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ) +{ +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SchXMLBodyContext_Impl::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + return new SchXMLBodyContext( mrImportHelper, GetImport(), nElement ); +} + +SchXMLDocContext::SchXMLDocContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + sal_Int32 nElement ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ) +{ + SAL_WARN_IF(( nElement != XML_ELEMENT( OFFICE, XML_DOCUMENT ) && + nElement != XML_ELEMENT( OFFICE, XML_DOCUMENT_META ) && + nElement != XML_ELEMENT( OFFICE, XML_DOCUMENT_STYLES ) && + nElement != XML_ELEMENT( OFFICE, XML_DOCUMENT_CONTENT ) ), "xmloff.chart", "SchXMLDocContext instantiated with no <office:document> element" ); +} + +SchXMLDocContext::~SchXMLDocContext() +{} + + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SchXMLDocContext::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& /*xAttrList*/ ) +{ + SvXMLImportFlags nFlags = GetImport().getImportFlags(); + switch (nElement) + { + case XML_ELEMENT(OFFICE, XML_BODY): + if( nFlags & SvXMLImportFlags::CONTENT ) + return new SchXMLBodyContext_Impl( mrImportHelper, GetImport() ); + break; + case XML_ELEMENT(OFFICE, XML_STYLES): + // for draw styles containing gradients/hatches/markers and dashes + if( nFlags & SvXMLImportFlags::STYLES ) + return new SvXMLStylesContext( GetImport() ); + break; + case XML_ELEMENT(OFFICE, XML_AUTOMATIC_STYLES): + if( nFlags & SvXMLImportFlags::AUTOSTYLES ) + // not nice, but this is safe, as the SchXMLDocContext class can only by + // instantiated by the chart import class SchXMLImport (header is not exported) + return + static_cast< SchXMLImport& >( GetImport() ).CreateStylesContext(); + break; + case XML_ELEMENT(OFFICE, XML_META): + // we come here in the flat ODF file format, + // if XDocumentPropertiesSupplier is not supported at the model + break; + } + return nullptr; +} + +SchXMLFlatDocContext_Impl::SchXMLFlatDocContext_Impl( + SchXMLImportHelper& i_rImpHelper, + SchXMLImport& i_rImport, + sal_Int32 i_nElement, + const uno::Reference<document::XDocumentProperties>& i_xDocProps) : + SvXMLImportContext(i_rImport), + SchXMLDocContext(i_rImpHelper, i_rImport, i_nElement), + SvXMLMetaDocumentContext(i_rImport, i_xDocProps) +{ +} + +uno::Reference< xml::sax::XFastContextHandler > SAL_CALL SchXMLFlatDocContext_Impl::createFastChildContext( + sal_Int32 nElement, const uno::Reference< xml::sax::XFastAttributeList >& xAttrList ) +{ + // behave like meta base class iff we encounter office:meta + if ( nElement == XML_ELEMENT( OFFICE, XML_META ) ) { + return SvXMLMetaDocumentContext::createFastChildContext( + nElement, xAttrList ); + } else { + return SchXMLDocContext::createFastChildContext( + nElement, xAttrList ); + } +} + +SchXMLBodyContext::SchXMLBodyContext( SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + sal_Int32 nElement ) : + SvXMLImportContext( rImport ), + mrImportHelper( rImpHelper ) +{ + SAL_WARN_IF( nElement != XML_ELEMENT(OFFICE, XML_CHART), "xmloff.chart", "SchXMLBodyContext instantiated with no <office:chart> element" ); +} + +SchXMLBodyContext::~SchXMLBodyContext() +{} + +css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLBodyContext::createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) +{ + css::uno::Reference< css::xml::sax::XFastContextHandler > xContext; + + // <chart:chart> element + if( nElement == XML_ELEMENT(CHART, XML_CHART) ) + { + xContext = mrImportHelper.CreateChartContext( GetImport(), GetImport().GetModel() ); + } + else if(nElement == XML_ELEMENT(TABLE, XML_CALCULATION_SETTINGS )) + { + // i99104 handle null date correctly + xContext = new SchXMLCalculationSettingsContext ( GetImport(), xAttrList); + } + else + XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); + + return xContext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/contexts.hxx b/xmloff/source/chart/contexts.hxx new file mode 100644 index 000000000..4a710bcc5 --- /dev/null +++ b/xmloff/source/chart/contexts.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <SchXMLImport.hxx> +#include <xmloff/xmlictxt.hxx> + +#include <xmloff/xmlmetai.hxx> + +namespace com::sun::star::xml::sax { + class XAttributeList; +} + +/* + These contexts are only needed by + SchXMLImport not by the SchXMLImportHelper + that is also used by other applications +*/ + +class SchXMLDocContext : public virtual SvXMLImportContext +{ +protected: + SchXMLImportHelper& mrImportHelper; + +public: + SchXMLDocContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + sal_Int32 nElement ); + + virtual ~SchXMLDocContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +// context for flat file xml format +class SchXMLFlatDocContext_Impl + : public SchXMLDocContext, public SvXMLMetaDocumentContext +{ +public: + SchXMLFlatDocContext_Impl( + SchXMLImportHelper& i_rImpHelper, + SchXMLImport& i_rImport, + sal_Int32 i_nElement, + const css::uno::Reference<css::document::XDocumentProperties>& i_xDocProps); + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) override; +}; + +class SchXMLBodyContext : public SvXMLImportContext +{ +private: + SchXMLImportHelper& mrImportHelper; + +public: + SchXMLBodyContext( + SchXMLImportHelper& rImpHelper, + SvXMLImport& rImport, + sal_Int32 nElement ); + virtual ~SchXMLBodyContext() override; + + virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext( + sal_Int32 nElement, + const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/transporttypes.cxx b/xmloff/source/chart/transporttypes.cxx new file mode 100644 index 000000000..c648413be --- /dev/null +++ b/xmloff/source/chart/transporttypes.cxx @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include "transporttypes.hxx" + +bool operator < ( const tSchXMLIndexWithPart & rFirst, const tSchXMLIndexWithPart & rSecond ) +{ + if( rFirst.first == rSecond.first ) + return (static_cast< int >( rFirst.second ) < static_cast< int >( rSecond.second )); + return (rFirst.first < rSecond.first); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmloff/source/chart/transporttypes.hxx b/xmloff/source/chart/transporttypes.hxx new file mode 100644 index 000000000..7053d20c8 --- /dev/null +++ b/xmloff/source/chart/transporttypes.hxx @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include <com/sun/star/chart2/XDataSeries.hpp> +#include <com/sun/star/chart2/data/XLabeledDataSequence.hpp> + +#include <vector> +#include <map> +#include <optional> + +enum SchXMLCellType +{ + SCH_CELL_TYPE_UNKNOWN, + SCH_CELL_TYPE_FLOAT, + SCH_CELL_TYPE_STRING, + SCH_CELL_TYPE_COMPLEX_STRING +}; + +struct SchXMLCell +{ + OUString aString; + css::uno::Sequence< OUString > aComplexString; + double fValue; + SchXMLCellType eType; + OUString aRangeId; + + SchXMLCell(): fValue( 0.0 ), eType( SCH_CELL_TYPE_UNKNOWN ) + {} +}; + +struct SchXMLTable +{ + std::vector< std::vector< SchXMLCell > > aData; /// an array of rows containing the table contents + + sal_Int32 nRowIndex; /// reflects the index of the row currently parsed + sal_Int32 nColumnIndex; /// reflects the index of the column currently parsed + sal_Int32 nMaxColumnIndex; /// the greatest number of columns detected + + sal_Int32 nNumberOfColsEstimate; /// parsing column-elements may yield an estimate + + bool bHasHeaderRow; + bool bHasHeaderColumn; + + OUString aTableNameOfFile; /// the table name read at the table:table element + + ::std::vector< sal_Int32 > aHiddenColumns; + + bool bProtected; + + SchXMLTable() : nRowIndex( -1 ), + nColumnIndex( -1 ), + nMaxColumnIndex( -1 ), + nNumberOfColsEstimate( 0 ), + bHasHeaderRow( false ), + bHasHeaderColumn( false ), + bProtected( false ) + {} +}; + +typedef sal_Int32 tSchXMLIndex; +#define SCH_XML_CATEGORIES_INDEX (static_cast<tSchXMLIndex>(-1)) +enum SchXMLLabeledSequencePart +{ + SCH_XML_PART_LABEL, + SCH_XML_PART_VALUES, + SCH_XML_PART_ERROR_BARS +}; +typedef ::std::pair< tSchXMLIndex, SchXMLLabeledSequencePart > tSchXMLIndexWithPart; +typedef ::std::multimap< tSchXMLIndexWithPart, + css::uno::Reference< css::chart2::data::XLabeledDataSequence > > + tSchXMLLSequencesPerIndex; + +bool operator < ( const tSchXMLIndexWithPart & rFirst, const tSchXMLIndexWithPart & rSecond ); + +enum SchXMLAxisDimension +{ + SCH_XML_AXIS_X = 0, + SCH_XML_AXIS_Y, + SCH_XML_AXIS_Z, + SCH_XML_AXIS_UNDEF +}; + +struct SchXMLAxis +{ + enum SchXMLAxisDimension eDimension; + sal_Int8 nAxisIndex;//0->primary axis; 1->secondary axis + OUString aName; + OUString aTitle; + bool bHasCategories; + + SchXMLAxis() : eDimension( SCH_XML_AXIS_UNDEF ), nAxisIndex( 0 ), bHasCategories( false ) {} +}; + +struct GlobalSeriesImportInfo +{ + explicit GlobalSeriesImportInfo( bool& rAllRangeAddressesAvailable ) + : rbAllRangeAddressesAvailable( rAllRangeAddressesAvailable ) + , nCurrentDataIndex( 0 ) + , nFirstFirstDomainIndex( -1 ) + , nFirstSecondDomainIndex( -1 ) + {} + + bool& rbAllRangeAddressesAvailable; + + sal_Int32 nCurrentDataIndex; + + OUString aFirstFirstDomainAddress; + sal_Int32 nFirstFirstDomainIndex; + + OUString aFirstSecondDomainAddress; + sal_Int32 nFirstSecondDomainIndex; +}; + +struct RegressionStyle +{ + css::uno::Reference< + css::chart2::XDataSeries > m_xSeries; + css::uno::Reference< + css::beans::XPropertySet > m_xEquationProperties; + + OUString msStyleName; + + RegressionStyle(const css::uno::Reference< + css::chart2::XDataSeries >& xSeries, + const OUString& sStyleName) : + m_xSeries ( xSeries ), + msStyleName ( sStyleName ) + {} +}; + +/** + * Used to store text content of a data point custom-label's fields and also + * the helper members that indicates whether this label's contents are sourced + * from a cell[range] and the address of the cell[range] with GUID of + * the CELLRANGE field. + */ +struct CustomLabelsInfo +{ + /// Text content of each field. + ::std::vector<OUString> mLabels; + /// Are label's contents sourced from a cell[range] ? + bool mbDataLabelsRange = false; + /// GUID of the CELLRANGE field. + OUString msLabelGuid; + /// cell[range] from which label's contents are sourced. + OUString msLabelsCellRange; +}; + +struct DataRowPointStyle +{ + enum StyleType + { + DATA_POINT, + DATA_SERIES, + MEAN_VALUE, + ERROR_INDICATOR, + DATA_LABEL_POINT, + DATA_LABEL_SERIES + }; + + StyleType meType; + css::uno::Reference< css::chart2::XDataSeries > m_xSeries; + + css::uno::Reference< css::beans::XPropertySet > m_xOldAPISeries; + + css::uno::Reference< css::beans::XPropertySet > m_xErrorXProperties; + + css::uno::Reference< css::beans::XPropertySet > m_xErrorYProperties; + + sal_Int32 m_nPointIndex; + sal_Int32 m_nPointRepeat; + OUString msStyleName; + OUString msStyleNameOfParent; // e.g. target of line and fill styles of data-labels + CustomLabelsInfo mCustomLabels; + double mCustomLabelPos[2] = { 0.0, 0.0 }; + // for svg:x and svg:y attribute (in core unit), of element <chart:data-label> + std::optional<sal_Int32> mo_nLabelAbsolutePosX; + std::optional<sal_Int32> mo_nLabelAbsolutePosY; + OUString msSeriesStyleNameForDonuts; + + sal_Int32 mnAttachedAxis; + bool mbSymbolSizeForSeriesIsMissingInFile; + + DataRowPointStyle( StyleType eType + , const css::uno::Reference< css::chart2::XDataSeries >& xSeries + , sal_Int32 nPointIndex + , sal_Int32 nPointRepeat + , const OUString& sStyleName + , sal_Int32 nAttachedAxis = 0 ) : + meType( eType ), + m_xSeries( xSeries ), + m_nPointIndex( nPointIndex ), + m_nPointRepeat( nPointRepeat ), + msStyleName( sStyleName ), + mnAttachedAxis( nAttachedAxis ), + mbSymbolSizeForSeriesIsMissingInFile( false ) + {} + + // ctor for use in import of <chart:data-label> as child of <chart:series> + DataRowPointStyle(StyleType eType, const OUString& sStyleName, sal_Int32 nAttachedAxis = 0) + : meType(eType) + , m_nPointIndex(0) + , m_nPointRepeat(0) + , msStyleName(sStyleName) + , mnAttachedAxis(nAttachedAxis) + , mbSymbolSizeForSeriesIsMissingInFile(false) + { + } +}; + +typedef ::std::multimap< OUString, css::uno::Reference< + css::chart2::data::XDataSequence > > tSchXMLRangeSequenceMap; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |