Avoid potential negative array index access to cached text.
[LibreOffice.git] / xmloff / source / chart / SchXMLTools.cxx
blob120e361f84f0053042495442451d360ab2d99d50
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "SchXMLTools.hxx"
22 #include <rtl/ustrbuf.hxx>
23 #include <xmloff/xmluconv.hxx>
24 #include <xmloff/xmlement.hxx>
25 #include <xmloff/xmlimppr.hxx>
26 #include <xmloff/prstylei.hxx>
27 #include <xmloff/xmlprmap.hxx>
28 #include <xmloff/xmlexp.hxx>
29 #include <xmloff/xmlnamespace.hxx>
30 #include <xmloff/xmlmetai.hxx>
31 #include <xmloff/maptype.hxx>
33 #include <com/sun/star/beans/PropertyAttribute.hpp>
34 #include <com/sun/star/uno/XComponentContext.hpp>
35 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
36 #include <com/sun/star/chart2/data/XDataProvider.hpp>
37 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
38 #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
39 #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
40 #include <com/sun/star/chart2/XChartDocument.hpp>
41 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
42 #include <com/sun/star/container/XChild.hpp>
43 #include <com/sun/star/document/XDocumentProperties.hpp>
44 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
45 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
46 #include <com/sun/star/xml/sax/XDocumentHandler.hpp>
48 #include <comphelper/processfactory.hxx>
49 #include <comphelper/diagnose_ex.hxx>
50 #include <sal/log.hxx>
51 #include <o3tl/string_view.hxx>
52 #include <algorithm>
53 #include <map>
55 using namespace com::sun::star;
56 using namespace ::xmloff::token;
58 using ::com::sun::star::uno::Reference;
59 using ::com::sun::star::uno::Sequence;
61 namespace
64 OUString lcl_getGeneratorFromModel( const uno::Reference< frame::XModel >& xChartModel )
66 OUString aGenerator;
67 uno::Reference< document::XDocumentPropertiesSupplier> xChartDocumentPropertiesSupplier( xChartModel, uno::UNO_QUERY );
68 if( xChartDocumentPropertiesSupplier.is() )
70 uno::Reference< document::XDocumentProperties > xChartDocumentProperties(
71 xChartDocumentPropertiesSupplier->getDocumentProperties());
72 if( xChartDocumentProperties.is() )
73 aGenerator = xChartDocumentProperties->getGenerator();
75 return aGenerator;
78 OUString lcl_getGeneratorFromModelOrItsParent( const uno::Reference< frame::XModel >& xChartModel )
80 OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
81 if( aGenerator.isEmpty() ) //try to get the missing info from the parent document
83 uno::Reference< container::XChild > xChild( xChartModel, uno::UNO_QUERY );
84 if( xChild.is() )
85 aGenerator = lcl_getGeneratorFromModel( uno::Reference< frame::XModel >( xChild->getParent(), uno::UNO_QUERY) );
87 return aGenerator;
90 sal_Int32 lcl_getBuildIDFromGenerator( std::u16string_view rGenerator )
92 //returns -1 if nothing found
93 sal_Int32 nBuildId = -1;
94 static constexpr OUString sBuildCompare( u"$Build-"_ustr );
95 size_t nBegin = rGenerator.find( sBuildCompare );
96 if( nBegin != std::u16string_view::npos )
98 std::u16string_view sBuildId = rGenerator.substr( nBegin + sBuildCompare.getLength() );
99 nBuildId = o3tl::toInt32(sBuildId);
101 return nBuildId;
104 OUString lcl_ConvertRange( const OUString & rRange, const Reference< chart2::data::XDataProvider >& xDataProvider )
106 OUString aResult = rRange;
107 Reference< chart2::data::XRangeXMLConversion > xRangeConversion( xDataProvider, uno::UNO_QUERY );
108 if( xRangeConversion.is())
109 aResult = xRangeConversion->convertRangeFromXML( rRange );
110 return aResult;
113 Reference< chart2::data::XDataSequence > lcl_createNewSequenceFromCachedXMLRange( const Reference< chart2::data::XDataSequence >& xSeq, const Reference< chart2::data::XDataProvider >& xDataProvider )
115 Reference< chart2::data::XDataSequence > xRet;
116 OUString aRange;
117 if( xSeq.is() && SchXMLTools::getXMLRangePropertyFromDataSequence( xSeq, aRange, /* bClearProp = */ true ) )
119 xRet.set( xDataProvider->createDataSequenceByRangeRepresentation(
120 lcl_ConvertRange( aRange, xDataProvider )) );
121 SchXMLTools::copyProperties( Reference< beans::XPropertySet >( xSeq, uno::UNO_QUERY ),
122 Reference< beans::XPropertySet >( xRet, uno::UNO_QUERY ));
124 return xRet;
127 } // anonymous namespace
129 namespace SchXMLTools
132 const SvXMLEnumMapEntry<SchXMLChartTypeEnum> aXMLChartClassMap[] =
134 { XML_LINE, XML_CHART_CLASS_LINE },
135 { XML_AREA, XML_CHART_CLASS_AREA },
136 { XML_CIRCLE, XML_CHART_CLASS_CIRCLE },
137 { XML_RING, XML_CHART_CLASS_RING },
138 { XML_SCATTER, XML_CHART_CLASS_SCATTER },
139 { XML_RADAR, XML_CHART_CLASS_RADAR },
140 { XML_FILLED_RADAR, XML_CHART_CLASS_FILLED_RADAR },
141 { XML_BAR, XML_CHART_CLASS_BAR },
142 { XML_STOCK, XML_CHART_CLASS_STOCK },
143 { XML_BUBBLE, XML_CHART_CLASS_BUBBLE },
144 { XML_SURFACE, XML_CHART_CLASS_BAR }, //@todo change this if a surface chart is available
145 { XML_ADD_IN, XML_CHART_CLASS_ADDIN },
146 { XML_TOKEN_INVALID, XML_CHART_CLASS_UNKNOWN }
149 SchXMLChartTypeEnum GetChartTypeEnum( std::u16string_view rClassName )
151 SchXMLChartTypeEnum nEnumVal = XML_CHART_CLASS_UNKNOWN;
152 SvXMLUnitConverter::convertEnum( nEnumVal, rClassName, aXMLChartClassMap );
153 return nEnumVal;
156 typedef std::map< OUString, OUString > tMakeStringStringMap;
157 //static
158 static const tMakeStringStringMap& lcl_getChartTypeNameMap()
160 //shape property -- chart model object property
161 static const tMakeStringStringMap g_aChartTypeNameMap{
162 {"com.sun.star.chart.LineDiagram",
163 "com.sun.star.chart2.LineChartType"},
164 {"com.sun.star.chart.AreaDiagram",
165 "com.sun.star.chart2.AreaChartType"},
166 {"com.sun.star.chart.BarDiagram",
167 "com.sun.star.chart2.ColumnChartType"},
168 {"com.sun.star.chart.PieDiagram",
169 "com.sun.star.chart2.PieChartType"},
170 {"com.sun.star.chart.DonutDiagram",
171 "com.sun.star.chart2.DonutChartType"},
172 {"com.sun.star.chart.XYDiagram",
173 "com.sun.star.chart2.ScatterChartType"},
174 {"com.sun.star.chart.NetDiagram",
175 "com.sun.star.chart2.NetChartType"},
176 {"com.sun.star.chart.FilledNetDiagram",
177 "com.sun.star.chart2.FilledNetChartType"},
178 {"com.sun.star.chart.StockDiagram",
179 "com.sun.star.chart2.CandleStickChartType"},
180 {"com.sun.star.chart.BubbleDiagram",
181 "com.sun.star.chart2.BubbleChartType"}};
182 return g_aChartTypeNameMap;
185 OUString GetNewChartTypeName( const OUString & rOldChartTypeName )
187 OUString aNew(rOldChartTypeName);
189 const tMakeStringStringMap& rMap = lcl_getChartTypeNameMap();
190 tMakeStringStringMap::const_iterator aIt( rMap.find( rOldChartTypeName ));
191 if( aIt != rMap.end())
193 aNew = aIt->second;
195 return aNew;
198 OUString GetChartTypeByClassName(
199 std::u16string_view rClassName, bool bUseOldNames )
201 OUStringBuffer aResultBuffer;
202 bool bInternalType = false;
204 if( bUseOldNames )
205 aResultBuffer.append( "com.sun.star.chart.");
206 else
207 aResultBuffer.append( "com.sun.star.chart2.");
209 bInternalType = true;
211 if( IsXMLToken( rClassName, XML_LINE ))
212 aResultBuffer.append("Line");
213 else if( IsXMLToken( rClassName, XML_AREA ))
214 aResultBuffer.append("Area");
215 else if( IsXMLToken( rClassName, XML_BAR ))
217 if( bUseOldNames )
218 aResultBuffer.append("Bar");
219 else
221 aResultBuffer.append("Column");
222 // @todo: might be Bar
225 else if( IsXMLToken( rClassName, XML_CIRCLE ))
226 aResultBuffer.append("Pie");
227 else if( IsXMLToken( rClassName, XML_RING ))
228 aResultBuffer.append("Donut");
229 else if( IsXMLToken( rClassName, XML_SCATTER ))
231 if( bUseOldNames )
232 aResultBuffer.append("XY");
233 else
234 aResultBuffer.append("Scatter");
237 else if( IsXMLToken( rClassName, XML_BUBBLE ))
238 aResultBuffer.append("Bubble");
239 else if( IsXMLToken( rClassName, XML_RADAR ))
240 aResultBuffer.append("Net");
241 else if( IsXMLToken( rClassName, XML_FILLED_RADAR ))
242 aResultBuffer.append("FilledNet");
243 else if( IsXMLToken( rClassName, XML_STOCK ))
245 if( bUseOldNames )
246 aResultBuffer.append("Stock");
247 else
248 aResultBuffer.append("CandleStick");
250 else if( IsXMLToken( rClassName, XML_SURFACE ))
252 //@todo change this if a surface chart is available
253 if( bUseOldNames )
254 aResultBuffer.append("Bar");
255 else
256 aResultBuffer.append("Column");
258 else
259 bInternalType = false;
261 if( ! bInternalType )
262 return OUString();
264 if( bUseOldNames )
265 aResultBuffer.append("Diagram");
266 else
267 aResultBuffer.append("ChartType");
269 return aResultBuffer.makeStringAndClear();
273 XMLTokenEnum getTokenByChartType(
274 std::u16string_view rChartTypeService, bool bUseOldNames )
276 XMLTokenEnum eResult = XML_TOKEN_INVALID;
277 OUString aPrefix, aPostfix;
279 if( bUseOldNames )
281 aPrefix = "com.sun.star.chart.";
282 aPostfix = "Diagram";
284 else
286 aPrefix = "com.sun.star.chart2.";
287 aPostfix = "ChartType";
290 if( o3tl::starts_with(rChartTypeService, aPrefix))
292 sal_Int32 nSkip = aPrefix.getLength();
293 SAL_WARN_IF( static_cast<sal_Int32>(rChartTypeService.size()) < nSkip, "xmloff.chart", "ChartTypeService.getLength() < nSkip" );
294 sal_Int32 nTypeLength = rChartTypeService.size() - nSkip - aPostfix.getLength();
295 // if postfix matches and leaves a non-empty type
296 if( nTypeLength > 0 && o3tl::starts_with(rChartTypeService.substr(nSkip + nTypeLength), aPostfix) )
298 std::u16string_view aServiceName( rChartTypeService.substr( nSkip, nTypeLength ));
300 if ( aServiceName == u"Line" )
301 eResult = XML_LINE;
302 else if ( aServiceName == u"Area" )
303 eResult = XML_AREA;
304 else if( aServiceName == u"Bar" ||
305 (!bUseOldNames && aServiceName == u"Column"))
306 eResult = XML_BAR;
307 else if ( aServiceName == u"Pie" )
308 eResult = XML_CIRCLE;
309 else if ( aServiceName == u"Donut" )
310 eResult = XML_RING;
311 else if( (bUseOldNames && aServiceName == u"XY") ||
312 (!bUseOldNames && aServiceName == u"Scatter"))
313 eResult = XML_SCATTER;
314 else if ( aServiceName == u"Bubble" )
315 eResult = XML_BUBBLE;
316 else if ( aServiceName == u"Net" )
317 eResult = XML_RADAR;
318 else if ( aServiceName == u"FilledNet" )
319 eResult = XML_FILLED_RADAR;
320 else if( (bUseOldNames && aServiceName == u"Stock") ||
321 (!bUseOldNames && aServiceName == u"CandleStick"))
322 eResult = XML_STOCK;
326 if( eResult == XML_TOKEN_INVALID && !rChartTypeService.empty() )
327 eResult = XML_ADD_IN;
329 return eResult;
332 Reference< chart2::data::XLabeledDataSequence2 > GetNewLabeledDataSequence()
334 Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
335 Reference< chart2::data::XLabeledDataSequence2 > xResult = chart2::data::LabeledDataSequence::create(xContext);
336 return xResult;
339 Reference< chart2::data::XDataSequence > CreateDataSequence(
340 const OUString & rRange,
341 const Reference< chart2::XChartDocument >& xChartDoc )
343 Reference< chart2::data::XDataSequence > xRet;
345 if( !xChartDoc.is() )
347 SAL_WARN("xmloff.chart", "need a chart document" );
348 return xRet;
351 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
352 if( !xDataProvider.is() )
354 SAL_WARN("xmloff.chart", "need a data provider" );
355 return xRet;
358 bool bUseInternal = false;
359 uno::Reference<beans::XPropertySet> xPropSet(xDataProvider, uno::UNO_QUERY);
360 if (xPropSet.is())
364 bool bVal = false;
365 uno::Any any = xPropSet->getPropertyValue("UseInternalDataProvider");
366 if (any >>= bVal)
367 bUseInternal = bVal;
369 catch (const beans::UnknownPropertyException&)
371 // Do nothing
375 if (!bUseInternal)
379 xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( lcl_ConvertRange( rRange, xDataProvider )));
380 SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange );
382 catch( const lang::IllegalArgumentException & )
384 SAL_WARN("xmloff.chart", "could not create data sequence" );
388 if( !xRet.is() && !xChartDoc->hasInternalDataProvider() && !rRange.isEmpty() )
390 //#i103911# switch to internal data in case the parent cannot provide the requested data
391 xChartDoc->createInternalDataProvider( true /* bCloneExistingData */ );
392 xDataProvider = xChartDoc->getDataProvider();
395 xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( lcl_ConvertRange( rRange, xDataProvider )));
396 SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange );
398 catch( const lang::IllegalArgumentException & )
400 SAL_WARN("xmloff.chart", "could not create data sequence" );
403 return xRet;
406 Reference< chart2::data::XDataSequence > CreateDataSequenceWithoutConvert(
407 const OUString & rRange,
408 const Reference< chart2::XChartDocument >& xChartDoc )
410 Reference< chart2::data::XDataSequence > xRet;
412 if( !xChartDoc.is() )
414 SAL_WARN("xmloff.chart", "need a chart document" );
415 return xRet;
418 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
419 if( !xDataProvider.is() )
421 SAL_WARN("xmloff.chart", "need a data provider" );
422 return xRet;
427 xRet.set( xDataProvider->createDataSequenceByRangeRepresentation( rRange ) );
428 SchXMLTools::setXMLRangePropertyAtDataSequence( xRet, rRange );
430 catch( const lang::IllegalArgumentException & )
432 SAL_WARN("xmloff.chart", "could not create data sequence" );
435 return xRet;
438 void CreateCategories(
439 const uno::Reference< chart2::data::XDataProvider > & xDataProvider,
440 const uno::Reference< chart2::XChartDocument > & xNewDoc,
441 const OUString & rRangeAddress,
442 sal_Int32 nCooSysIndex,
443 sal_Int32 nDimensionIndex,
444 tSchXMLLSequencesPerIndex * pLSequencesPerIndex )
448 if( xNewDoc.is() && !rRangeAddress.isEmpty())
450 if( xDataProvider.is())
452 uno::Reference< chart2::XDiagram > xDia( xNewDoc->getFirstDiagram());
453 if( !xDia.is())
454 return;
456 uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDia, uno::UNO_QUERY_THROW );
457 uno::Sequence< uno::Reference< chart2::XCoordinateSystem > >
458 aCooSysSeq( xCooSysCnt->getCoordinateSystems());
459 if( nCooSysIndex < aCooSysSeq.getLength())
461 uno::Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[nCooSysIndex] );
462 SAL_WARN_IF( !xCooSys.is(), "xmloff.chart", "xCooSys is NULL");
463 if( nDimensionIndex < xCooSys->getDimension() )
465 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex);
466 for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
468 uno::Reference< chart2::XAxis > xAxis( xCooSys->getAxisByDimension( nDimensionIndex, nI ));
469 if( xAxis.is() )
471 chart2::ScaleData aData( xAxis->getScaleData());
472 uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
473 GetNewLabeledDataSequence(), uno::UNO_QUERY_THROW);
476 OUString aConvertedRange( rRangeAddress );
477 bool bRangeConverted = false;
478 if( ! (xNewDoc->hasInternalDataProvider() && aConvertedRange == "categories"))
480 Reference< chart2::data::XRangeXMLConversion > xXMLConv( xDataProvider, uno::UNO_QUERY );
481 if( xXMLConv.is())
483 aConvertedRange = xXMLConv->convertRangeFromXML( rRangeAddress );
484 bRangeConverted = true;
488 Reference<chart2::data::XDataSequence> xSequence;
489 Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xDataProvider, uno::UNO_QUERY);
490 if (xPivotTableDataProvider.is())
492 xSequence.set(xPivotTableDataProvider->createDataSequenceOfCategories());
494 else
496 xSequence.set(xDataProvider->createDataSequenceByRangeRepresentation(aConvertedRange));
497 if (bRangeConverted)
498 setXMLRangePropertyAtDataSequence(xSequence, rRangeAddress);
500 xLabeledSeq->setValues(xSequence);
503 catch( const lang::IllegalArgumentException & )
505 DBG_UNHANDLED_EXCEPTION("xmloff.chart");
507 aData.Categories.set( xLabeledSeq );
508 if( pLSequencesPerIndex )
510 // register for setting local data if external data provider is not present
511 pLSequencesPerIndex->emplace(
512 tSchXMLIndexWithPart( SCH_XML_CATEGORIES_INDEX, SCH_XML_PART_VALUES ), xLabeledSeq );
514 xAxis->setScaleData( aData );
522 catch( uno::Exception & )
524 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception caught while creating Categories" );
528 uno::Any getPropertyFromContext( std::u16string_view rPropertyName, const XMLPropStyleContext* pPropStyleContext, const SvXMLStylesContext* pStylesCtxt )
530 uno::Any aRet;
531 if( !pPropStyleContext || !pStylesCtxt )
532 return aRet;
533 const ::std::vector< XMLPropertyState >& rProperties = pPropStyleContext->GetProperties();
534 const rtl::Reference< XMLPropertySetMapper >& rMapper = pStylesCtxt->GetImportPropertyMapper( pPropStyleContext->GetFamily()/*XML_STYLE_FAMILY_SCH_CHART_ID*/ )->getPropertySetMapper();
535 for( const auto& rProp : rProperties )
537 sal_Int32 nIdx = rProp.mnIndex;
538 if( nIdx == -1 )
539 continue;
540 OUString aPropName = rMapper->GetEntryAPIName( nIdx );
541 if(rPropertyName == aPropName)
542 return rProp.maValue;
544 return aRet;
547 void exportText( SvXMLExport& rExport, const OUString& rText, bool bConvertTabsLFs )
549 SvXMLElementExport aPara( rExport, XML_NAMESPACE_TEXT,
550 ::xmloff::token::GetXMLToken( ::xmloff::token::XML_P ),
551 true, false );
553 if( bConvertTabsLFs )
555 sal_Int32 nStartPos = 0;
556 sal_Int32 nEndPos = rText.getLength();
557 sal_Unicode cChar;
559 for( sal_Int32 nPos = 0; nPos < nEndPos; nPos++ )
561 cChar = rText[ nPos ];
562 switch( cChar )
564 case 0x0009: // tabulator
566 if( nPos > nStartPos )
567 rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) );
568 nStartPos = nPos + 1;
570 SvXMLElementExport aElem( rExport, XML_NAMESPACE_TEXT,
571 ::xmloff::token::GetXMLToken( ::xmloff::token::XML_TAB_STOP ),
572 false, false );
574 break;
576 case 0x000A: // linefeed
578 if( nPos > nStartPos )
579 rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nPos - nStartPos)) );
580 nStartPos = nPos + 1;
582 SvXMLElementExport aElem( rExport, XML_NAMESPACE_TEXT,
583 ::xmloff::token::GetXMLToken( ::xmloff::token::XML_LINE_BREAK ),
584 false, false );
586 break;
589 if( nEndPos > nStartPos )
591 if( nStartPos == 0 )
592 rExport.GetDocHandler()->characters( rText );
593 else
594 rExport.GetDocHandler()->characters( rText.copy( nStartPos, (nEndPos - nStartPos)) );
597 else // do not convert tabs and linefeeds (eg for numbers coming from unit converter)
599 rExport.GetDocHandler()->characters( rText );
603 void exportRangeToSomewhere( SvXMLExport& rExport, const OUString& rValue )
605 //with issue #i366# and CWS chart20 ranges for error bars were introduced
606 //to keep them during copy paste from calc to impress for example it
607 //was necessary to introduce a mapping between the used ranges within calc and the data written to the local table
608 //this is why we write this ranges here
610 //#i113950# first the range was exported to attribute text:id, but that attribute does not allow arbitrary strings anymore within ODF 1.2
611 //as an alternative the range info is now saved into the description at an empty group element (not very nice, but ODF conform)
613 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
614 rExport.getSaneDefaultVersion());
615 if (nCurrentODFVersion == SvtSaveOptions::ODFSVER_010 || nCurrentODFVersion == SvtSaveOptions::ODFSVER_011)
616 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
618 SvXMLElementExport aEmptyShapeGroup( rExport, XML_NAMESPACE_DRAW,
619 ::xmloff::token::GetXMLToken( ::xmloff::token::XML_G ),
620 true, false );
621 SvXMLElementExport aDescription( rExport, XML_NAMESPACE_SVG,
622 ::xmloff::token::GetXMLToken( ::xmloff::token::XML_DESC ),
623 true, false );
624 rExport.GetDocHandler()->characters( rValue );
627 void setXMLRangePropertyAtDataSequence(
628 const Reference< chart2::data::XDataSequence > & xDataSequence,
629 const OUString & rXMLRange )
631 if( !xDataSequence.is())
632 return;
635 static constexpr OUString aXMLRangePropName( u"CachedXMLRange"_ustr );
636 Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW );
637 Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo());
638 if( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName ))
639 xProp->setPropertyValue( aXMLRangePropName, uno::Any( rXMLRange ));
641 catch( const uno::Exception & )
643 DBG_UNHANDLED_EXCEPTION("xmloff.chart");
647 bool getXMLRangePropertyFromDataSequence(
648 const Reference< chart2::data::XDataSequence > & xDataSequence,
649 OUString & rOutXMLRange,
650 bool bClearProp /* = false */)
652 bool bResult = false;
653 if( xDataSequence.is())
657 static constexpr OUString aXMLRangePropName( u"CachedXMLRange"_ustr );
658 Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY_THROW );
659 Reference< beans::XPropertySetInfo > xInfo( xProp->getPropertySetInfo());
660 bResult =
661 ( xInfo.is() && xInfo->hasPropertyByName( aXMLRangePropName ) &&
662 ( xProp->getPropertyValue( aXMLRangePropName ) >>= rOutXMLRange ) &&
663 !rOutXMLRange.isEmpty());
664 // clear the property after usage
665 if( bClearProp && bResult )
666 xProp->setPropertyValue( aXMLRangePropName, uno::Any( OUString()));
668 catch( const uno::Exception & )
670 DBG_UNHANDLED_EXCEPTION("xmloff.chart");
673 return bResult;
676 void copyProperties(
677 const Reference< beans::XPropertySet > & xSource,
678 const Reference< beans::XPropertySet > & xDestination )
680 if( ! (xSource.is() && xDestination.is()) )
681 return;
685 Reference< beans::XPropertySetInfo > xSrcInfo( xSource->getPropertySetInfo(), uno::UNO_SET_THROW );
686 Reference< beans::XPropertySetInfo > xDestInfo( xDestination->getPropertySetInfo(), uno::UNO_SET_THROW );
687 const Sequence< beans::Property > aProperties( xSrcInfo->getProperties());
688 for( const auto& rProperty : aProperties )
690 OUString aName( rProperty.Name);
691 if( xDestInfo->hasPropertyByName( aName ))
693 beans::Property aProp( xDestInfo->getPropertyByName( aName ));
694 if( (aProp.Attributes & beans::PropertyAttribute::READONLY) == 0 )
695 xDestination->setPropertyValue(
696 aName, xSource->getPropertyValue( aName ));
700 catch( const uno::Exception & )
702 SAL_WARN("xmloff.chart", "Copying property sets failed!" );
706 bool switchBackToDataProviderFromParent( const Reference< chart2::XChartDocument >& xChartDoc, const tSchXMLLSequencesPerIndex & rLSequencesPerIndex )
708 //return whether the switch is successful
709 if( !xChartDoc.is() || !xChartDoc->hasInternalDataProvider() )
710 return false;
711 Reference< chart2::data::XDataProvider > xDataProviderFromParent( SchXMLTools::getDataProviderFromParent( xChartDoc ) );
712 if( !xDataProviderFromParent.is() )
713 return false;
714 uno::Reference< chart2::data::XDataReceiver > xDataReceiver( xChartDoc, uno::UNO_QUERY );
715 if( !xDataReceiver.is() )
716 return false;
718 xDataReceiver->attachDataProvider( xDataProviderFromParent );
720 for( const auto& rLSeq : rLSequencesPerIndex )
722 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( rLSeq.second );
723 if( !xLabeledSeq.is() )
724 continue;
725 Reference< chart2::data::XDataSequence > xNewSeq;
726 xNewSeq = lcl_createNewSequenceFromCachedXMLRange( xLabeledSeq->getValues(), xDataProviderFromParent );
727 if( xNewSeq.is() )
728 xLabeledSeq->setValues( xNewSeq );
729 xNewSeq = lcl_createNewSequenceFromCachedXMLRange( xLabeledSeq->getLabel(), xDataProviderFromParent );
730 if( xNewSeq.is() )
731 xLabeledSeq->setLabel( xNewSeq );
733 return true;
736 void setBuildIDAtImportInfo( const uno::Reference< frame::XModel >& xModel, const Reference< beans::XPropertySet >& xImportInfo )
738 OUString aGenerator( lcl_getGeneratorFromModelOrItsParent(xModel) );
739 if( !aGenerator.isEmpty() )
740 SvXMLMetaDocumentContext::setBuildId( aGenerator, xImportInfo );
743 bool isDocumentGeneratedWithOpenOfficeOlderThan3_3( const uno::Reference< frame::XModel >& xChartModel )
745 bool bResult = isDocumentGeneratedWithOpenOfficeOlderThan3_0( xChartModel );
746 if( !bResult )
748 OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
749 if( aGenerator.indexOf( "OpenOffice.org_project/3" ) != -1 )
751 if( aGenerator.indexOf( "OpenOffice.org_project/300m" ) != -1 )
753 sal_Int32 nBuilId = lcl_getBuildIDFromGenerator( lcl_getGeneratorFromModel(xChartModel) );
754 if( nBuilId>0 && nBuilId<9491 ) //9491 is build id of dev300m76
755 bResult= true;
757 else if( aGenerator.indexOf( "OpenOffice.org_project/310m" ) != -1 )
758 bResult= true;
759 else if( aGenerator.indexOf( "OpenOffice.org_project/320m" ) != -1 )
760 bResult= true;
763 return bResult;
766 bool isDocumentGeneratedWithOpenOfficeOlderThan3_0( const uno::Reference< frame::XModel >& xChartModel )
768 bool bResult = isDocumentGeneratedWithOpenOfficeOlderThan2_3( xChartModel );
769 if( !bResult )
771 OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
772 if( aGenerator.indexOf( "OpenOffice.org_project/680m" ) != -1 )
773 bResult= true;
775 return bResult;
778 bool isDocumentGeneratedWithOpenOfficeOlderThan2_4( const uno::Reference< frame::XModel >& xChartModel )
780 if( isDocumentGeneratedWithOpenOfficeOlderThan2_3( xChartModel ) )
781 return true;
783 if( isDocumentGeneratedWithOpenOfficeOlderThan3_0( xChartModel ) )
785 sal_Int32 nBuilId = lcl_getBuildIDFromGenerator( lcl_getGeneratorFromModel(xChartModel) );
786 if( nBuilId>0 && nBuilId<=9238 ) //9238 is build id of OpenOffice.org 2.3.1
787 return true;
789 return false;
792 bool isDocumentGeneratedWithOpenOfficeOlderThan2_3( const uno::Reference< frame::XModel >& xChartModel )
794 bool bResult = false;
795 OUString aGenerator( lcl_getGeneratorFromModel(xChartModel) );
796 //if there is a meta stream at the chart object it was not written with an older OpenOffice version < 2.3
797 if( aGenerator.isEmpty() )
799 //if there is no meta stream at the chart object we need to check whether the parent document is OpenOffice at all
800 uno::Reference< container::XChild > xChild( xChartModel, uno::UNO_QUERY );
801 if( xChild.is() )
803 aGenerator = lcl_getGeneratorFromModel( uno::Reference< frame::XModel >( xChild->getParent(), uno::UNO_QUERY) );
804 if( aGenerator.indexOf( "OpenOffice.org_project" ) != -1 )
806 //the chart application has not created files without a meta stream since OOo 2.3 (OOo 2.3 has written a metastream already)
807 //only the report builder extension has created some files with OOo 3.1 that do not have a meta stream
808 if( aGenerator.indexOf( "OpenOffice.org_project/31" ) != -1 )
809 bResult = false;//#i100102# probably generated with OOo 3.1 by the report designer
810 else
811 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
813 else if( isDocumentGeneratedWithOpenOfficeOlderThan2_0(xChartModel) )
814 bResult= true;
817 return bResult;
820 bool isDocumentGeneratedWithOpenOfficeOlderThan2_0( const css::uno::Reference< css::frame::XModel >& xChartModel)
822 bool bResult = false;
823 OUString aGenerator( lcl_getGeneratorFromModelOrItsParent(xChartModel) );
824 if( aGenerator.startsWith( "OpenOffice.org 1" )
825 || aGenerator.startsWith( "StarOffice 6" )
826 || aGenerator.startsWith( "StarOffice 7" )
827 || aGenerator.startsWith( "StarSuite 6" )
828 || aGenerator.startsWith( "StarSuite 7" )
830 bResult= true;
831 return bResult;
834 Reference< chart2::data::XDataProvider > getDataProviderFromParent( const Reference< chart2::XChartDocument >& xChartDoc )
836 Reference< chart2::data::XDataProvider > xRet;
837 uno::Reference< container::XChild > xChild( xChartDoc, uno::UNO_QUERY );
838 if( xChild.is() )
840 Reference< lang::XMultiServiceFactory > xFact( xChild->getParent(), uno::UNO_QUERY );
841 if( xFact.is() )
843 static constexpr OUString aDataProviderServiceName( u"com.sun.star.chart2.data.DataProvider"_ustr);
844 const uno::Sequence< OUString > aServiceNames( xFact->getAvailableServiceNames());
845 const OUString * pBegin = aServiceNames.getConstArray();
846 const OUString * pEnd = pBegin + aServiceNames.getLength();
847 if( ::std::find( pBegin, pEnd, aDataProviderServiceName ) != pEnd )
849 xRet.set( xFact->createInstance( aDataProviderServiceName ), uno::UNO_QUERY );
853 return xRet;
856 } // namespace SchXMLTools
858 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */