1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include "SchXMLChartContext.hxx" 21 #include <SchXMLImport.hxx> 22 #include "SchXMLLegendContext.hxx" 23 #include "SchXMLDataTableContext.hxx" 24 #include "SchXMLPlotAreaContext.hxx" 25 #include "SchXMLParagraphContext.hxx" 26 #include "SchXMLTableContext.hxx" 27 #include "SchXMLSeries2Context.hxx" 28 #include "SchXMLTools.hxx" 29 #include <osl/diagnose.h> 30 #include <sal/log.hxx> 31 #include <comphelper/diagnose_ex.hxx> 32 #include <unotools/mediadescriptor.hxx> 33 #include <utility> 34 #include <xmloff/xmlnamespace.hxx> 35 #include <xmloff/xmltoken.hxx> 36 #include <xmloff/namespacemap.hxx> 37 #include <xmloff/xmluconv.hxx> 38 #include <xmloff/xmlstyle.hxx> 39 #include <xmloff/SchXMLSeriesHelper.hxx> 40 41 #include <vector> 42 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 43 #include <com/sun/star/chart/XChartDocument.hpp> 44 #include <com/sun/star/chart/XDiagram.hpp> 45 #include <com/sun/star/drawing/XDrawPageSupplier.hpp> 46 #include <com/sun/star/embed/Aspects.hpp> 47 #include <com/sun/star/embed/XVisualObject.hpp> 48 49 #include <com/sun/star/chart2/XChartDocument.hpp> 50 #include <com/sun/star/chart2/data/XDataSink.hpp> 51 #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp> 52 #include <com/sun/star/chart2/XDataSeriesContainer.hpp> 53 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> 54 #include <com/sun/star/chart2/XChartTypeContainer.hpp> 55 #include <com/sun/star/chart2/XTitled.hpp> 56 57 #include <com/sun/star/container/XChild.hpp> 58 #include <com/sun/star/chart2/data/XDataReceiver.hpp> 59 #include <o3tl/safeint.hxx> 60 #include <o3tl/string_view.hxx> 61 62 using namespace com::sun::star; 63 using namespace ::xmloff::token; 64 using com::sun::star::uno::Reference; 65 using namespace ::SchXMLTools; 66 67 namespace 68 { 69 70 void lcl_setRoleAtLabeledSequence( 71 const uno::Reference< chart2::data::XLabeledDataSequence > & xLSeq, 72 const OUString &rRole ) 73 { 74 // set role of sequence 75 uno::Reference< chart2::data::XDataSequence > xValues( xLSeq->getValues()); 76 if( xValues.is()) 77 { 78 uno::Reference< beans::XPropertySet > xProp( xValues, uno::UNO_QUERY ); 79 if( xProp.is()) 80 xProp->setPropertyValue("Role", uno::Any( rRole )); 81 } 82 } 83 84 void lcl_MoveDataToCandleStickSeries( 85 const uno::Reference< chart2::data::XDataSource > & xDataSource, 86 const uno::Reference< chart2::XDataSeries > & xDestination, 87 const OUString & rRole ) 88 { 89 try 90 { 91 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledSeq( 92 xDataSource->getDataSequences()); 93 if( aLabeledSeq.hasElements()) 94 { 95 lcl_setRoleAtLabeledSequence( aLabeledSeq[0], rRole ); 96 97 // add to data series 98 uno::Reference< chart2::data::XDataSource > xSource( xDestination, uno::UNO_QUERY_THROW ); 99 // @todo: realloc only once outside this function 100 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aData( xSource->getDataSequences()); 101 aData.realloc( aData.getLength() + 1); 102 aData.getArray()[ aData.getLength() - 1 ] = aLabeledSeq[0]; 103 uno::Reference< chart2::data::XDataSink > xSink( xDestination, uno::UNO_QUERY_THROW ); 104 xSink->setData( aData ); 105 } 106 } 107 catch(const uno::Exception&) 108 { 109 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception caught while moving data to candlestick series" ); 110 } 111 } 112 113 void lcl_setRoleAtFirstSequence( 114 const uno::Reference< chart2::XDataSeries > & xSeries, 115 const OUString & rRole ) 116 { 117 uno::Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY ); 118 if( xSource.is()) 119 { 120 uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aSeq( xSource->getDataSequences()); 121 if( aSeq.hasElements()) 122 lcl_setRoleAtLabeledSequence( aSeq[0], rRole ); 123 } 124 } 125 126 void lcl_removeEmptyChartTypeGroups( const uno::Reference< chart2::XChartDocument > & xDoc ) 127 { 128 if( ! xDoc.is()) 129 return; 130 131 uno::Reference< chart2::XDiagram > xDia( xDoc->getFirstDiagram()); 132 if( ! xDia.is()) 133 return; 134 135 try 136 { 137 // count all charttype groups to be able to leave at least one 138 sal_Int32 nRemainingGroups = 0; 139 uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDia, uno::UNO_QUERY_THROW ); 140 const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > 141 aCooSysSeq( xCooSysCnt->getCoordinateSystems()); 142 for( auto const & i : aCooSysSeq ) 143 { 144 uno::Reference< chart2::XChartTypeContainer > xCTCnt( i, uno::UNO_QUERY_THROW ); 145 nRemainingGroups += xCTCnt->getChartTypes().getLength(); 146 } 147 148 // delete all empty groups, but leave at least group (empty or not) 149 for( sal_Int32 nI = aCooSysSeq.getLength(); nI-- && (nRemainingGroups > 1); ) 150 { 151 uno::Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nI], uno::UNO_QUERY_THROW ); 152 uno::Sequence< uno::Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes()); 153 for( sal_Int32 nJ=aCTSeq.getLength(); nJ-- && (nRemainingGroups > 1); ) 154 { 155 uno::Reference< chart2::XDataSeriesContainer > xDSCnt( aCTSeq[nJ], uno::UNO_QUERY_THROW ); 156 if( !xDSCnt->getDataSeries().hasElements() ) 157 { 158 // note: iterator stays valid as we have a local sequence 159 xCTCnt->removeChartType( aCTSeq[nJ] ); 160 --nRemainingGroups; 161 } 162 } 163 } 164 } 165 catch(const uno::Exception&) 166 { 167 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught while removing empty chart types"); 168 } 169 } 170 171 uno::Sequence< sal_Int32 > lcl_getNumberSequenceFromString( std::u16string_view rStr, bool bAddOneToEachOldIndex ) 172 { 173 const sal_Unicode aSpace( ' ' ); 174 175 // count number of entries 176 ::std::vector< sal_Int32 > aVec; 177 size_t nLastPos = 0; 178 size_t nPos = 0; 179 while( nPos != std::u16string_view::npos ) 180 { 181 nPos = rStr.find( aSpace, nLastPos ); 182 if( nPos != std::u16string_view::npos ) 183 { 184 if( nPos > nLastPos ) 185 aVec.push_back( o3tl::toInt32(rStr.substr( nLastPos, (nPos - nLastPos) )) ); 186 nLastPos = nPos + 1; 187 } 188 } 189 // last entry 190 if( nLastPos != 0 && 191 rStr.size() > nLastPos ) 192 { 193 aVec.push_back( o3tl::toInt32(rStr.substr( nLastPos )) ); 194 } 195 196 const size_t nVecSize = aVec.size(); 197 uno::Sequence< sal_Int32 > aSeq( nVecSize ); 198 199 if(!bAddOneToEachOldIndex) 200 { 201 sal_Int32* pSeqArr = aSeq.getArray(); 202 for( nPos = 0; nPos < nVecSize; ++nPos ) 203 { 204 pSeqArr[ nPos ] = aVec[ nPos ]; 205 } 206 } 207 else if( bAddOneToEachOldIndex ) 208 { 209 aSeq.realloc( nVecSize+1 ); 210 auto pSeqArr = aSeq.getArray(); 211 pSeqArr[0]=0; 212 213 for( nPos = 0; nPos < nVecSize; ++nPos ) 214 { 215 pSeqArr[ nPos+1 ] = aVec[ nPos ]+1; 216 } 217 } 218 219 return aSeq; 220 } 221 222 } // anonymous namespace 223 224 SchXMLChartContext::SchXMLChartContext( SchXMLImportHelper& rImpHelper, 225 SvXMLImport& rImport ) : 226 SvXMLImportContext( rImport ), 227 mrImportHelper( rImpHelper ), 228 m_bHasRangeAtPlotArea( false ), 229 m_bHasTableElement( false ), 230 mbAllRangeAddressesAvailable( true ), 231 mbColHasLabels( false ), 232 mbRowHasLabels( false ), 233 meDataRowSource( chart::ChartDataRowSource_COLUMNS ), 234 mbIsStockChart( false ), 235 mPieSubType(com::sun::star::chart2::PieChartSubType_NONE) 236 { 237 } 238 239 SchXMLChartContext::~SchXMLChartContext() 240 {} 241 242 static bool lcl_hasServiceName(Reference<lang::XMultiServiceFactory> const & xFactory, OUString const & rServiceName) 243 { 244 const uno::Sequence<OUString> aServiceNames(xFactory->getAvailableServiceNames()); 245 246 return std::find(aServiceNames.begin(), aServiceNames.end(), rServiceName) != aServiceNames.end(); 247 } 248 249 void setDataProvider(uno::Reference<chart2::XChartDocument> const & xChartDoc, OUString const & sDataPilotSource) 250 { 251 if (!xChartDoc.is()) 252 return; 253 254 try 255 { 256 uno::Reference<container::XChild> xChild(xChartDoc, uno::UNO_QUERY); 257 uno::Reference<chart2::data::XDataReceiver> xDataReceiver(xChartDoc, uno::UNO_QUERY); 258 if (xChild.is() && xDataReceiver.is()) 259 { 260 bool bHasOwnData = true; 261 262 Reference<lang::XMultiServiceFactory> xFact(xChild->getParent(), uno::UNO_QUERY); 263 if (xFact.is()) 264 { 265 if (!xChartDoc->getDataProvider().is()) 266 { 267 bool bHasDataPilotSource = !sDataPilotSource.isEmpty(); 268 OUString aDataProviderServiceName("com.sun.star.chart2.data.DataProvider"); 269 if (bHasDataPilotSource) 270 aDataProviderServiceName = "com.sun.star.chart2.data.PivotTableDataProvider"; 271 272 if (lcl_hasServiceName(xFact, aDataProviderServiceName)) 273 { 274 Reference<chart2::data::XDataProvider> xProvider(xFact->createInstance(aDataProviderServiceName), uno::UNO_QUERY); 275 276 if (xProvider.is()) 277 { 278 if (bHasDataPilotSource) 279 { 280 Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xProvider, uno::UNO_QUERY); 281 xPivotTableDataProvider->setPivotTableName(sDataPilotSource); 282 xDataReceiver->attachDataProvider(xProvider); 283 bHasOwnData = !xPivotTableDataProvider->hasPivotTable(); 284 } 285 else 286 { 287 xDataReceiver->attachDataProvider(xProvider); 288 bHasOwnData = false; 289 } 290 } 291 } 292 } 293 else 294 bHasOwnData = false; 295 } 296 // else we have no parent => we have our own data 297 298 if (bHasOwnData && ! xChartDoc->hasInternalDataProvider()) 299 xChartDoc->createInternalDataProvider(false); 300 } 301 } 302 catch (const uno::Exception &) 303 { 304 TOOLS_INFO_EXCEPTION("xmloff.chart", "SchXMLChartContext::StartElement()"); 305 } 306 } 307 308 void SchXMLChartContext::startFastElement( sal_Int32 /*nElement*/, 309 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) 310 { 311 // parse attributes 312 313 uno::Reference< embed::XVisualObject > xVisualObject( mrImportHelper.GetChartDocument(), uno::UNO_QUERY); 314 SAL_WARN_IF(!xVisualObject.is(), "xmloff.chart", "need xVisualObject for page size"); 315 if( xVisualObject.is() ) 316 maChartSize = xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); //#i103460# take the size given from the parent frame as default 317 318 OUString sAutoStyleName; 319 OUString aOldChartTypeName; 320 bool bHasAddin = false; 321 mPieSubType = com::sun::star::chart2::PieChartSubType_NONE; 322 323 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) 324 { 325 switch( aIter.getToken() ) 326 { 327 case XML_ELEMENT(LO_EXT, XML_DATA_PILOT_SOURCE): 328 msDataPilotSource = aIter.toString(); 329 break; 330 case XML_ELEMENT(XLINK, XML_HREF): 331 m_aXLinkHRefAttributeToIndicateDataProvider = aIter.toString(); 332 break; 333 case XML_ELEMENT(CHART, XML_CLASS): 334 { 335 OUString aValue = aIter.toString(); 336 OUString sClassName; 337 sal_uInt16 nClassPrefix = 338 GetImport().GetNamespaceMap().GetKeyByAttrValueQName( 339 aValue, &sClassName ); 340 if( XML_NAMESPACE_CHART == nClassPrefix ) 341 { 342 SchXMLChartTypeEnum eChartTypeEnum = SchXMLTools::GetChartTypeEnum( sClassName ); 343 if( eChartTypeEnum != XML_CHART_CLASS_UNKNOWN ) 344 { 345 aOldChartTypeName = SchXMLTools::GetChartTypeByClassName( sClassName, true /* bUseOldNames */ ); 346 maChartTypeServiceName = SchXMLTools::GetChartTypeByClassName( sClassName, false /* bUseOldNames */ ); 347 switch( eChartTypeEnum ) 348 { 349 case XML_CHART_CLASS_STOCK: 350 mbIsStockChart = true; 351 break; 352 default: 353 break; 354 } 355 } 356 } 357 else if( XML_NAMESPACE_OOO == nClassPrefix ) 358 { 359 // service is taken from add-in-name attribute 360 bHasAddin = true; 361 362 aOldChartTypeName = sClassName; 363 maChartTypeServiceName = sClassName; 364 } 365 } 366 break; 367 368 case XML_ELEMENT(SVG, XML_WIDTH): 369 case XML_ELEMENT(SVG_COMPAT, XML_WIDTH): 370 GetImport().GetMM100UnitConverter().convertMeasureToCore( 371 maChartSize.Width, aIter.toView() ); 372 break; 373 374 case XML_ELEMENT(SVG, XML_HEIGHT): 375 case XML_ELEMENT(SVG_COMPAT, XML_HEIGHT): 376 GetImport().GetMM100UnitConverter().convertMeasureToCore( 377 maChartSize.Height, aIter.toView() ); 378 break; 379 380 case XML_ELEMENT(CHART, XML_STYLE_NAME): 381 sAutoStyleName = aIter.toString(); 382 break; 383 384 case XML_ELEMENT(CHART, XML_COLUMN_MAPPING): 385 msColTrans = aIter.toString(); 386 break; 387 case XML_ELEMENT(CHART, XML_ROW_MAPPING): 388 msRowTrans = aIter.toString(); 389 break; 390 case XML_ELEMENT(LO_EXT, XML_SUB_BAR): 391 if (aIter.toString().toBoolean()) { 392 mPieSubType = com::sun::star::chart2::PieChartSubType_BAR; 393 } 394 break; 395 case XML_ELEMENT(LO_EXT, XML_SUB_PIE): 396 if (aIter.toString().toBoolean()) { 397 mPieSubType = com::sun::star::chart2::PieChartSubType_PIE; 398 } 399 break; 400 default: 401 XMLOFF_WARN_UNKNOWN("xmloff", aIter); 402 } 403 } 404 405 uno::Reference<chart::XChartDocument> xDoc = mrImportHelper.GetChartDocument(); 406 uno::Reference<chart2::XChartDocument> xNewDoc(xDoc, uno::UNO_QUERY); 407 408 setDataProvider(xNewDoc, msDataPilotSource); 409 410 if( aOldChartTypeName.isEmpty() ) 411 { 412 SAL_WARN("xmloff.chart", "need a charttype to create a diagram" ); 413 //set a fallback value: 414 const OUString& aChartClass_Bar( GetXMLToken(XML_BAR ) ); 415 aOldChartTypeName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, true /* bUseOldNames */ ); 416 maChartTypeServiceName = SchXMLTools::GetChartTypeByClassName( aChartClass_Bar, false /* bUseOldNames */ ); 417 } 418 419 // Set the size of the draw page. 420 if( xVisualObject.is() ) 421 xVisualObject->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, maChartSize ); 422 423 InitChart( aOldChartTypeName); 424 425 if( bHasAddin ) 426 { 427 //correct charttype service name when having an addin 428 //and don't refresh addin during load 429 uno::Reference< beans::XPropertySet > xDocProp( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ); 430 if( xDocProp.is() ) 431 { 432 try 433 { 434 xDocProp->getPropertyValue("BaseDiagram") >>= aOldChartTypeName; 435 maChartTypeServiceName = SchXMLTools::GetNewChartTypeName( aOldChartTypeName ); 436 xDocProp->setPropertyValue("RefreshAddInAllowed", uno::Any( false) ); 437 } 438 catch(const uno::Exception&) 439 { 440 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::StartElement" ); 441 } 442 } 443 } 444 445 // set auto-styles for Area 446 uno::Reference<beans::XPropertySet> xProp = mrImportHelper.GetChartDocument()->getArea(); 447 mrImportHelper.FillAutoStyle(sAutoStyleName, xProp); 448 } 449 450 namespace 451 { 452 453 struct NewDonutSeries 454 { 455 css::uno::Reference< css::chart2::XDataSeries > m_xSeries; 456 OUString msStyleName; 457 sal_Int32 mnAttachedAxis; 458 459 ::std::vector< OUString > m_aSeriesStyles; 460 ::std::vector< OUString > m_aPointStyles; 461 462 NewDonutSeries( css::uno::Reference< css::chart2::XDataSeries > xSeries, sal_Int32 nPointCount ) 463 : m_xSeries(std::move( xSeries )) 464 , mnAttachedAxis( 1 ) 465 { 466 m_aPointStyles.resize(nPointCount); 467 m_aSeriesStyles.resize(nPointCount); 468 } 469 470 void setSeriesStyleNameToPoint( const OUString& rStyleName, sal_Int32 nPointIndex ) 471 { 472 SAL_WARN_IF(nPointIndex >= static_cast<sal_Int32>(m_aSeriesStyles.size()), "xmloff.chart", "donut point <-> series count mismatch"); 473 if( nPointIndex < static_cast<sal_Int32>(m_aSeriesStyles.size()) ) 474 m_aSeriesStyles[nPointIndex]=rStyleName; 475 } 476 477 void setPointStyleNameToPoint( const OUString& rStyleName, sal_Int32 nPointIndex ) 478 { 479 SAL_WARN_IF(nPointIndex >= static_cast<sal_Int32>(m_aPointStyles.size()), "xmloff.chart", "donut point <-> series count mismatch"); 480 if( nPointIndex < static_cast<sal_Int32>(m_aPointStyles.size()) ) 481 m_aPointStyles[nPointIndex]=rStyleName; 482 } 483 484 ::std::vector< DataRowPointStyle > creatStyleVector() 485 { 486 ::std::vector< DataRowPointStyle > aRet; 487 488 DataRowPointStyle aSeriesStyle( DataRowPointStyle::DATA_SERIES 489 , m_xSeries, -1, 1, msStyleName, mnAttachedAxis ); 490 aRet.push_back( aSeriesStyle ); 491 492 sal_Int32 nPointIndex=0; 493 for (auto const& pointStyle : m_aPointStyles) 494 { 495 DataRowPointStyle aPointStyle( DataRowPointStyle::DATA_POINT 496 , m_xSeries, nPointIndex, 1, pointStyle, mnAttachedAxis ); 497 if( nPointIndex < static_cast<sal_Int32>(m_aSeriesStyles.size()) ) 498 { 499 aPointStyle.msSeriesStyleNameForDonuts = m_aSeriesStyles[nPointIndex]; 500 } 501 if( !aPointStyle.msSeriesStyleNameForDonuts.isEmpty() 502 || !aPointStyle.msStyleName.isEmpty() ) 503 aRet.push_back( aPointStyle ); 504 ++nPointIndex; 505 } 506 507 return aRet; 508 } 509 }; 510 511 void lcl_swapPointAndSeriesStylesForDonutCharts( ::std::vector< DataRowPointStyle >& rStyleVector 512 , ::std::map< css::uno::Reference< css::chart2::XDataSeries> , sal_Int32 >&& aSeriesMap ) 513 { 514 //detect old series count 515 //and add old series to aSeriesMap 516 sal_Int32 nOldSeriesCount = 0; 517 { 518 sal_Int32 nMaxOldSeriesIndex = 0; 519 sal_Int32 nOldSeriesIndex = 0; 520 for (auto const& style : rStyleVector) 521 { 522 DataRowPointStyle aStyle(style); 523 if(aStyle.meType == DataRowPointStyle::DATA_SERIES && 524 aStyle.m_xSeries.is() ) 525 { 526 nMaxOldSeriesIndex = nOldSeriesIndex; 527 528 if( aSeriesMap.end() == aSeriesMap.find(aStyle.m_xSeries) ) 529 aSeriesMap[aStyle.m_xSeries] = nOldSeriesIndex; 530 531 nOldSeriesIndex++; 532 } 533 } 534 nOldSeriesCount = nMaxOldSeriesIndex+1; 535 } 536 537 //initialize new series styles 538 ::std::map< Reference< chart2::XDataSeries >, sal_Int32 >::const_iterator aSeriesMapEnd( aSeriesMap.end() ); 539 540 //sort by index 541 ::std::vector< NewDonutSeries > aNewSeriesVector; 542 { 543 ::std::map< sal_Int32, Reference< chart2::XDataSeries > > aIndexSeriesMap; 544 for (auto const& series : aSeriesMap) 545 aIndexSeriesMap[series.second] = series.first; 546 547 for (auto const& indexSeries : aIndexSeriesMap) 548 aNewSeriesVector.emplace_back(indexSeries.second,nOldSeriesCount ); 549 } 550 551 //overwrite attached axis information according to old series styles 552 for (auto const& style : rStyleVector) 553 { 554 DataRowPointStyle aStyle(style); 555 if(aStyle.meType == DataRowPointStyle::DATA_SERIES ) 556 { 557 auto aSeriesMapIt = aSeriesMap.find( aStyle.m_xSeries ); 558 if( aSeriesMapIt != aSeriesMapEnd && aSeriesMapIt->second < static_cast<sal_Int32>(aNewSeriesVector.size()) ) 559 aNewSeriesVector[aSeriesMapIt->second].mnAttachedAxis = aStyle.mnAttachedAxis; 560 } 561 } 562 563 //overwrite new series style names with old series style name information 564 for (auto const& style : rStyleVector) 565 { 566 DataRowPointStyle aStyle(style); 567 if( aStyle.meType == DataRowPointStyle::DATA_SERIES ) 568 { 569 auto aSeriesMapIt = aSeriesMap.find(aStyle.m_xSeries); 570 if( aSeriesMapEnd != aSeriesMapIt ) 571 { 572 sal_Int32 nNewPointIndex = aSeriesMapIt->second; 573 574 for (auto & newSeries : aNewSeriesVector) 575 newSeries.setSeriesStyleNameToPoint( aStyle.msStyleName, nNewPointIndex ); 576 } 577 } 578 } 579 580 //overwrite new series style names with point style name information 581 for (auto const& style : rStyleVector) 582 { 583 DataRowPointStyle aStyle(style); 584 if( aStyle.meType == DataRowPointStyle::DATA_POINT ) 585 { 586 auto aSeriesMapIt = aSeriesMap.find(aStyle.m_xSeries); 587 if( aSeriesMapEnd != aSeriesMapIt ) 588 { 589 sal_Int32 nNewPointIndex = aSeriesMapIt->second; 590 sal_Int32 nNewSeriesIndex = aStyle.m_nPointIndex; 591 sal_Int32 nRepeatCount = aStyle.m_nPointRepeat; 592 593 while( nRepeatCount && (nNewSeriesIndex>=0) && (o3tl::make_unsigned(nNewSeriesIndex)< aNewSeriesVector.size() ) ) 594 { 595 NewDonutSeries& rNewSeries( aNewSeriesVector[nNewSeriesIndex] ); 596 rNewSeries.setPointStyleNameToPoint( aStyle.msStyleName, nNewPointIndex ); 597 598 nRepeatCount--; 599 nNewSeriesIndex++; 600 } 601 } 602 } 603 } 604 605 //put information from aNewSeriesVector to output parameter rStyleVector 606 rStyleVector.clear(); 607 608 for (auto & newSeries : aNewSeriesVector) 609 { 610 ::std::vector< DataRowPointStyle > aVector( newSeries.creatStyleVector() ); 611 rStyleVector.insert(rStyleVector.end(),aVector.begin(),aVector.end()); 612 } 613 } 614 615 bool lcl_SpecialHandlingForDonutChartNeeded( 616 std::u16string_view rServiceName, 617 const SvXMLImport & rImport ) 618 { 619 bool bResult = false; 620 if( rServiceName == u"com.sun.star.chart2.DonutChartType" ) 621 { 622 bResult = SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( rImport.GetModel() ); 623 } 624 return bResult; 625 } 626 627 } // anonymous namespace 628 629 static void lcl_ApplyDataFromRectangularRangeToDiagram( 630 const uno::Reference< chart2::XChartDocument >& xNewDoc 631 , const OUString& rRectangularRange 632 , css::chart::ChartDataRowSource eDataRowSource 633 , bool bRowHasLabels, bool bColHasLabels 634 , bool bSwitchOnLabelsAndCategoriesForOwnData 635 , std::u16string_view sColTrans 636 , std::u16string_view sRowTrans ) 637 { 638 if( !xNewDoc.is() ) 639 return; 640 641 uno::Reference< chart2::XDiagram > xNewDia( xNewDoc->getFirstDiagram()); 642 uno::Reference< chart2::data::XDataProvider > xDataProvider( xNewDoc->getDataProvider() ); 643 if( !xNewDia.is() || !xDataProvider.is() ) 644 return; 645 646 bool bFirstCellAsLabel = 647 (eDataRowSource==chart::ChartDataRowSource_COLUMNS)? bRowHasLabels : bColHasLabels; 648 bool bHasCateories = 649 (eDataRowSource==chart::ChartDataRowSource_COLUMNS)? bColHasLabels : bRowHasLabels; 650 651 if( bSwitchOnLabelsAndCategoriesForOwnData ) 652 { 653 bFirstCellAsLabel = true; 654 bHasCateories = true; 655 } 656 657 uno::Sequence< beans::PropertyValue > aArgs{ 658 beans::PropertyValue( 659 "CellRangeRepresentation", 660 -1, uno::Any( rRectangularRange ), 661 beans::PropertyState_DIRECT_VALUE ), 662 beans::PropertyValue( 663 "DataRowSource", 664 -1, uno::Any( eDataRowSource ), 665 beans::PropertyState_DIRECT_VALUE ), 666 beans::PropertyValue( 667 "FirstCellAsLabel", 668 -1, uno::Any( bFirstCellAsLabel ), 669 beans::PropertyState_DIRECT_VALUE ) 670 }; 671 672 if( !sColTrans.empty() || !sRowTrans.empty() ) 673 { 674 aArgs.realloc( aArgs.getLength() + 1 ); 675 aArgs.getArray()[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue( 676 "SequenceMapping", 677 -1, uno::Any( !sColTrans.empty() 678 ? lcl_getNumberSequenceFromString( sColTrans, bHasCateories && !xNewDoc->hasInternalDataProvider() ) 679 : lcl_getNumberSequenceFromString( sRowTrans, bHasCateories && !xNewDoc->hasInternalDataProvider() ) ), 680 beans::PropertyState_DIRECT_VALUE ); 681 } 682 683 //work around wrong writer ranges ( see Issue 58464 ) 684 { 685 OUString aChartOleObjectName; 686 if( xNewDoc.is() ) 687 { 688 utl::MediaDescriptor aMediaDescriptor( xNewDoc->getArgs() ); 689 690 utl::MediaDescriptor::const_iterator aIt( 691 aMediaDescriptor.find( OUString( "HierarchicalDocumentName" ))); 692 if( aIt != aMediaDescriptor.end() ) 693 { 694 aChartOleObjectName = (*aIt).second.get< OUString >(); 695 } 696 } 697 if( !aChartOleObjectName.isEmpty() ) 698 { 699 aArgs.realloc( aArgs.getLength() + 1 ); 700 aArgs.getArray()[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue( 701 "ChartOleObjectName", 702 -1, uno::Any( aChartOleObjectName ), 703 beans::PropertyState_DIRECT_VALUE ); 704 } 705 } 706 707 uno::Reference< chart2::data::XDataSource > xDataSource( 708 xDataProvider->createDataSource( aArgs )); 709 710 aArgs.realloc( aArgs.getLength() + 2 ); 711 auto pArgs = aArgs.getArray(); 712 pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 2 ] = beans::PropertyValue( 713 "HasCategories", 714 -1, uno::Any( bHasCateories ), 715 beans::PropertyState_DIRECT_VALUE ); 716 pArgs[ sal::static_int_cast<sal_uInt32>(aArgs.getLength()) - 1 ] = beans::PropertyValue( 717 "UseCategoriesAsX", 718 -1, uno::Any( false ),//categories in ODF files are not to be used as x values (independent from what is offered in our ui) 719 beans::PropertyState_DIRECT_VALUE ); 720 721 xNewDia->setDiagramData( xDataSource, aArgs ); 722 } 723 724 void SchXMLChartContext::endFastElement(sal_Int32 ) 725 { 726 uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument(); 727 uno::Reference< beans::XPropertySet > xProp( xDoc, uno::UNO_QUERY ); 728 uno::Reference< chart2::XChartDocument > xNewDoc( xDoc, uno::UNO_QUERY ); 729 730 if( xProp.is()) 731 { 732 if( !maMainTitle.isEmpty()) 733 { 734 uno::Reference< beans::XPropertySet > xTitleProp( xDoc->getTitle(), uno::UNO_QUERY ); 735 if( xTitleProp.is()) 736 { 737 try 738 { 739 // TODO: ODF import for formatted chart titles 740 xTitleProp->setPropertyValue("String", uno::Any(maMainTitle) ); 741 } 742 catch(const beans::UnknownPropertyException&) 743 { 744 SAL_WARN("xmloff.chart", "Property String for Title not available" ); 745 } 746 } 747 } 748 if( !maSubTitle.isEmpty()) 749 { 750 uno::Reference< beans::XPropertySet > xTitleProp( xDoc->getSubTitle(), uno::UNO_QUERY ); 751 if( xTitleProp.is()) 752 { 753 try 754 { 755 // TODO: ODF import for formatted chart titles 756 xTitleProp->setPropertyValue("String", uno::Any(maSubTitle) ); 757 } 758 catch(const beans::UnknownPropertyException&) 759 { 760 SAL_WARN("xmloff.chart", "Property String for Title not available" ); 761 } 762 } 763 } 764 } 765 766 // cleanup: remove empty chart type groups 767 lcl_removeEmptyChartTypeGroups( xNewDoc ); 768 769 // Handle sub-pie type. Is this the right place to do this? 770 if (maChartTypeServiceName == "com.sun.star.chart2.PieChartType") { 771 Reference< chart2::XDiagram> xDia(xNewDoc->getFirstDiagram()); 772 uno::Reference< beans::XPropertySet > xDiaProp( xDia, uno::UNO_QUERY ); 773 if( xDiaProp.is()) { 774 xDiaProp->setPropertyValue("SubPieType", uno::Any(mPieSubType)); 775 } 776 } 777 778 // set stack mode before a potential chart type detection (in case we have a rectangular range) 779 uno::Reference< chart::XDiagram > xDiagram( xDoc->getDiagram() ); 780 uno::Reference< beans::XPropertySet > xDiaProp( xDiagram, uno::UNO_QUERY ); 781 if( xDiaProp.is()) 782 { 783 if( maSeriesDefaultsAndStyles.maStackedDefault.hasValue()) 784 xDiaProp->setPropertyValue("Stacked",maSeriesDefaultsAndStyles.maStackedDefault); 785 if( maSeriesDefaultsAndStyles.maPercentDefault.hasValue()) 786 xDiaProp->setPropertyValue("Percent",maSeriesDefaultsAndStyles.maPercentDefault); 787 if( maSeriesDefaultsAndStyles.maDeepDefault.hasValue()) 788 xDiaProp->setPropertyValue("Deep",maSeriesDefaultsAndStyles.maDeepDefault); 789 if( maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault.hasValue()) 790 xDiaProp->setPropertyValue("StackedBarsConnected",maSeriesDefaultsAndStyles.maStackedBarsConnectedDefault); 791 } 792 793 //the OOo 2.0 implementation and older has a bug with donuts 794 bool bSpecialHandlingForDonutChart = lcl_SpecialHandlingForDonutChartNeeded( 795 maChartTypeServiceName, GetImport()); 796 797 // apply data 798 if(!xNewDoc.is()) 799 return; 800 801 bool bHasOwnData = false; 802 if( m_aXLinkHRefAttributeToIndicateDataProvider == "." ) //data comes from the chart itself 803 bHasOwnData = true; 804 else if( m_aXLinkHRefAttributeToIndicateDataProvider == ".." ) //data comes from the parent application 805 bHasOwnData = false; 806 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 807 bHasOwnData = m_bHasTableElement; 808 else 809 bHasOwnData = !m_bHasRangeAtPlotArea; 810 811 if( xNewDoc->hasInternalDataProvider()) 812 { 813 if( !m_bHasTableElement && m_aXLinkHRefAttributeToIndicateDataProvider != "." ) 814 { 815 //#i103147# ODF, workaround broken files with a missing table:cell-range-address at the plot-area 816 bool bSwitchSuccessful = SchXMLTools::switchBackToDataProviderFromParent( xNewDoc, maLSequencesPerIndex ); 817 bHasOwnData = !bSwitchSuccessful; 818 } 819 else 820 bHasOwnData = true;//e.g. in case of copy->paste from calc to impress 821 } 822 else if( bHasOwnData ) 823 { 824 xNewDoc->createInternalDataProvider( false /* bCloneExistingData */ ); 825 } 826 if( bHasOwnData ) 827 msChartAddress = "all"; 828 829 bool bSwitchRangesFromOuterToInternalIfNecessary = false; 830 if( !bHasOwnData && mbAllRangeAddressesAvailable ) 831 { 832 // special handling for stock chart (merge series together) 833 if( mbIsStockChart ) 834 MergeSeriesForStockChart(); 835 } 836 else if( !msChartAddress.isEmpty() ) 837 { 838 //own data or only rectangular range available 839 840 if( xNewDoc->hasInternalDataProvider() ) 841 SchXMLTableHelper::applyTableToInternalDataProvider( maTable, xNewDoc ); 842 843 bool bOlderThan2_3 = SchXMLTools::isDocumentGeneratedWithOpenOfficeOlderThan2_3( xNewDoc ); 844 bool bOldFileWithOwnDataFromRows = (bOlderThan2_3 && bHasOwnData && (meDataRowSource==chart::ChartDataRowSource_ROWS)); // in this case there are range addresses that are simply wrong. 845 846 if( mbAllRangeAddressesAvailable && !bSpecialHandlingForDonutChart && !mbIsStockChart && 847 !bOldFileWithOwnDataFromRows ) 848 { 849 //bHasOwnData is true in this case! 850 //e.g. for normal files with own data or also in case of copy paste scenario (e.g. calc to impress) 851 bSwitchRangesFromOuterToInternalIfNecessary = true; 852 } 853 else 854 { 855 //apply data from rectangular range 856 857 // create datasource from data provider with rectangular range parameters and change the diagram setDiagramData 858 try 859 { 860 if( bOlderThan2_3 && xDiaProp.is() )//for older charts the hidden cells were removed by calc on the fly 861 xDiaProp->setPropertyValue("IncludeHiddenCells",uno::Any(false)); 862 863 // note: mbRowHasLabels means the first row contains labels, that means we have "column-descriptions", 864 // (analogously mbColHasLabels means we have "row-descriptions") 865 lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans ); 866 } 867 catch(const uno::Exception&) 868 { 869 //try to fallback to internal data 870 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram try to fallback to internal data" ); 871 if(!bHasOwnData) 872 { 873 bHasOwnData = true; 874 msChartAddress = "all"; 875 if( !xNewDoc->hasInternalDataProvider() ) 876 { 877 xNewDoc->createInternalDataProvider( false /* bCloneExistingData */ ); 878 SchXMLTableHelper::applyTableToInternalDataProvider( maTable, xNewDoc ); 879 try 880 { 881 lcl_ApplyDataFromRectangularRangeToDiagram( xNewDoc, msChartAddress, meDataRowSource, mbRowHasLabels, mbColHasLabels, bHasOwnData, msColTrans, msRowTrans ); 882 } 883 catch(const uno::Exception&) 884 { 885 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception during import SchXMLChartContext::lcl_ApplyDataFromRectangularRangeToDiagram fallback to internal data failed also" ); 886 } 887 } 888 } 889 } 890 } 891 } 892 else 893 { 894 SAL_WARN("xmloff.chart", "Must not get here" ); 895 } 896 897 // now all series and data point properties are available and can be set 898 { 899 if( bSpecialHandlingForDonutChart ) 900 { 901 uno::Reference< chart2::XDiagram > xNewDiagram( xNewDoc->getFirstDiagram() ); 902 lcl_swapPointAndSeriesStylesForDonutCharts( maSeriesDefaultsAndStyles.maSeriesStyleVector 903 , SchXMLSeriesHelper::getDataSeriesIndexMapFromDiagram(xNewDiagram) ); 904 } 905 906 SchXMLSeries2Context::initSeriesPropertySets( maSeriesDefaultsAndStyles, xDoc ); 907 908 //set defaults from diagram to the new series: 909 //check whether we need to remove lines from symbol only charts 910 bool bSwitchOffLinesForScatter = false; 911 { 912 bool bLinesOn = true; 913 if( (maSeriesDefaultsAndStyles.maLinesOnProperty >>= bLinesOn) && !bLinesOn ) 914 { 915 if( maChartTypeServiceName == "com.sun.star.chart2.ScatterChartType" ) 916 { 917 bSwitchOffLinesForScatter = true; 918 SchXMLSeries2Context::switchSeriesLinesOff( maSeriesDefaultsAndStyles.maSeriesStyleVector ); 919 } 920 } 921 } 922 SchXMLSeries2Context::setDefaultsToSeries( maSeriesDefaultsAndStyles ); 923 924 // set autostyles for series and data points 925 const SvXMLStylesContext* pStylesCtxt = mrImportHelper.GetAutoStylesContext(); 926 const SvXMLStyleContext* pStyle = nullptr; 927 OUString sCurrStyleName; 928 929 if( pStylesCtxt ) 930 { 931 //iterate over data-series first 932 //don't set series styles for donut charts 933 if( !bSpecialHandlingForDonutChart ) 934 { 935 SchXMLSeries2Context::setStylesToSeries( 936 maSeriesDefaultsAndStyles, pStylesCtxt, pStyle, 937 sCurrStyleName, mrImportHelper, GetImport(), 938 mbIsStockChart, maLSequencesPerIndex ); 939 // ... then set attributes for statistics (after their existence was set in the series) 940 SchXMLSeries2Context::setStylesToStatisticsObjects( 941 maSeriesDefaultsAndStyles, pStylesCtxt, 942 pStyle, sCurrStyleName ); 943 944 SchXMLSeries2Context::setStylesToRegressionCurves( 945 maSeriesDefaultsAndStyles, pStylesCtxt, 946 pStyle, sCurrStyleName ); 947 } 948 } 949 950 //#i98319# call switchRangesFromOuterToInternalIfNecessary before the data point styles are applied, otherwise in copy->paste scenario the data point styles do get lost 951 if( bSwitchRangesFromOuterToInternalIfNecessary ) 952 { 953 if( xNewDoc->hasInternalDataProvider() ) 954 SchXMLTableHelper::switchRangesFromOuterToInternalIfNecessary( maTable, maLSequencesPerIndex, xNewDoc, meDataRowSource ); 955 } 956 957 if( pStylesCtxt ) 958 { 959 // ... then iterate over data-point attributes, so the latter are not overwritten 960 SchXMLSeries2Context::setStylesToDataPoints( maSeriesDefaultsAndStyles 961 , pStylesCtxt, pStyle, sCurrStyleName, mrImportHelper, GetImport(), mbIsStockChart, bSpecialHandlingForDonutChart, bSwitchOffLinesForScatter ); 962 } 963 } 964 965 if( xProp.is()) 966 xProp->setPropertyValue("RefreshAddInAllowed", uno::Any( true) ); 967 } 968 969 void SchXMLChartContext::MergeSeriesForStockChart() 970 { 971 OSL_ASSERT( mbIsStockChart ); 972 try 973 { 974 uno::Reference< chart::XChartDocument > xOldDoc( mrImportHelper.GetChartDocument()); 975 uno::Reference< chart2::XChartDocument > xDoc( xOldDoc, uno::UNO_QUERY_THROW ); 976 uno::Reference< chart2::XDiagram > xDiagram( xDoc->getFirstDiagram()); 977 if( ! xDiagram.is()) 978 return; 979 980 bool bHasJapaneseCandlestick = true; 981 uno::Reference< chart2::XDataSeriesContainer > xDSContainer; 982 uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW ); 983 const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems()); 984 for( const auto& rCooSys : aCooSysSeq ) 985 { 986 uno::Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY_THROW ); 987 const uno::Sequence< uno::Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes()); 988 auto pChartType = std::find_if(aChartTypes.begin(), aChartTypes.end(), 989 [](const auto& rChartType) { return rChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType"; }); 990 if (pChartType != aChartTypes.end()) 991 { 992 xDSContainer.set( *pChartType, uno::UNO_QUERY_THROW ); 993 uno::Reference< beans::XPropertySet > xCTProp( *pChartType, uno::UNO_QUERY_THROW ); 994 xCTProp->getPropertyValue("Japanese") >>= bHasJapaneseCandlestick; 995 } 996 } 997 998 if( xDSContainer.is()) 999 { 1000 // with japanese candlesticks: open, low, high, close 1001 // otherwise: low, high, close 1002 uno::Sequence< uno::Reference< chart2::XDataSeries > > aSeriesSeq( xDSContainer->getDataSeries()); 1003 const sal_Int32 nSeriesCount( aSeriesSeq.getLength()); 1004 const sal_Int32 nSeriesPerCandleStick = bHasJapaneseCandlestick ? 4: 3; 1005 sal_Int32 nCandleStickCount = nSeriesCount / nSeriesPerCandleStick; 1006 OSL_ASSERT( nSeriesPerCandleStick * nCandleStickCount == nSeriesCount ); 1007 uno::Sequence< uno::Reference< chart2::XDataSeries > > aNewSeries( nCandleStickCount ); 1008 auto aNewSeriesRange = asNonConstRange(aNewSeries); 1009 for( sal_Int32 i=0; i<nCandleStickCount; ++i ) 1010 { 1011 sal_Int32 nSeriesIndex = i*nSeriesPerCandleStick; 1012 if( bHasJapaneseCandlestick ) 1013 { 1014 // open values 1015 lcl_setRoleAtFirstSequence( aSeriesSeq[ nSeriesIndex ], "values-first"); 1016 aNewSeriesRange[i] = aSeriesSeq[ nSeriesIndex ]; 1017 // low values 1018 lcl_MoveDataToCandleStickSeries( 1019 uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ), 1020 aNewSeries[i], "values-min"); 1021 } 1022 else 1023 { 1024 // low values 1025 lcl_setRoleAtFirstSequence( aSeriesSeq[ nSeriesIndex ], "values-min"); 1026 aNewSeriesRange[i] = aSeriesSeq[ nSeriesIndex ]; 1027 } 1028 // high values 1029 lcl_MoveDataToCandleStickSeries( 1030 uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ), 1031 aNewSeries[i], "values-max"); 1032 // close values 1033 lcl_MoveDataToCandleStickSeries( 1034 uno::Reference< chart2::data::XDataSource >( aSeriesSeq[ ++nSeriesIndex ], uno::UNO_QUERY_THROW ), 1035 aNewSeries[i], "values-last"); 1036 } 1037 xDSContainer->setDataSeries( aNewSeries ); 1038 } 1039 } 1040 catch(const uno::Exception&) 1041 { 1042 TOOLS_WARN_EXCEPTION("xmloff.chart", "Exception while merging series for stock chart" ); 1043 } 1044 } 1045 1046 css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLChartContext::createFastChildContext( 1047 sal_Int32 nElement, 1048 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) 1049 { 1050 SvXMLImportContext* pContext = nullptr; 1051 uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument(); 1052 uno::Reference< beans::XPropertySet > xProp( xDoc, uno::UNO_QUERY ); 1053 1054 switch(nElement) 1055 { 1056 case XML_ELEMENT(CHART, XML_PLOT_AREA): 1057 pContext = new SchXMLPlotAreaContext( mrImportHelper, GetImport(), 1058 m_aXLinkHRefAttributeToIndicateDataProvider, 1059 msCategoriesAddress, 1060 msChartAddress, m_bHasRangeAtPlotArea, mbAllRangeAddressesAvailable, 1061 mbColHasLabels, mbRowHasLabels, 1062 meDataRowSource, 1063 maSeriesDefaultsAndStyles, 1064 maChartTypeServiceName, 1065 maLSequencesPerIndex, maChartSize ); 1066 break; 1067 case XML_ELEMENT(CHART, XML_TITLE): 1068 if( xDoc.is()) 1069 { 1070 if( xProp.is()) 1071 { 1072 xProp->setPropertyValue("HasMainTitle", uno::Any(true) ); 1073 } 1074 pContext = new SchXMLTitleContext( mrImportHelper, GetImport(), 1075 maMainTitle, xDoc->getTitle() ); 1076 } 1077 break; 1078 case XML_ELEMENT(CHART, XML_SUBTITLE): 1079 if( xDoc.is()) 1080 { 1081 if( xProp.is()) 1082 { 1083 xProp->setPropertyValue("HasSubTitle", uno::Any(true) ); 1084 } 1085 pContext = new SchXMLTitleContext( mrImportHelper, GetImport(), 1086 maSubTitle, xDoc->getSubTitle() ); 1087 } 1088 break; 1089 case XML_ELEMENT(CHART, XML_LEGEND): 1090 pContext = new SchXMLLegendContext( mrImportHelper, GetImport() ); 1091 break; 1092 case XML_ELEMENT(LO_EXT, XML_DATA_TABLE): 1093 pContext = new SchXMLDataTableContext(mrImportHelper, GetImport()); 1094 break; 1095 case XML_ELEMENT(TABLE, XML_TABLE): 1096 { 1097 SchXMLTableContext * pTableContext = 1098 new SchXMLTableContext( GetImport(), maTable ); 1099 m_bHasTableElement = true; 1100 // #i85913# take into account column- and row- mapping for 1101 // charts with own data only for those which were not copied 1102 // from a place where they got data from the container. Note, 1103 // that this requires the plot-area been read before the table 1104 // (which is required in the ODF spec) 1105 // Note: For stock charts and donut charts with special handling 1106 // the mapping must not be applied! 1107 if( msChartAddress.isEmpty() && !mbIsStockChart && 1108 !lcl_SpecialHandlingForDonutChartNeeded( 1109 maChartTypeServiceName, GetImport())) 1110 { 1111 if( !msColTrans.isEmpty() ) 1112 { 1113 OSL_ASSERT( msRowTrans.isEmpty() ); 1114 pTableContext->setColumnPermutation( lcl_getNumberSequenceFromString( msColTrans, true )); 1115 msColTrans.clear(); 1116 } 1117 else if( !msRowTrans.isEmpty() ) 1118 { 1119 pTableContext->setRowPermutation( lcl_getNumberSequenceFromString( msRowTrans, true )); 1120 msRowTrans.clear(); 1121 } 1122 } 1123 pContext = pTableContext; 1124 } 1125 break; 1126 1127 default: 1128 // try importing as an additional shape 1129 if( ! mxDrawPage.is()) 1130 { 1131 uno::Reference< drawing::XDrawPageSupplier > xSupp( xDoc, uno::UNO_QUERY ); 1132 if( xSupp.is()) 1133 mxDrawPage = xSupp->getDrawPage(); 1134 1135 SAL_WARN_IF( !mxDrawPage.is(), "xmloff.chart", "Invalid Chart Page" ); 1136 } 1137 if( mxDrawPage.is()) 1138 pContext = XMLShapeImportHelper::CreateGroupChildContext( 1139 GetImport(), nElement, xAttrList, mxDrawPage ); 1140 break; 1141 } 1142 1143 return pContext; 1144 } 1145 1146 /* 1147 With a locked controller the following is done here: 1148 1. Hide title, subtitle, and legend. 1149 2. Set the size of the draw page. 1150 3. Set a (logically) empty data set. 1151 4. Set the chart type. 1152 */ 1153 void SchXMLChartContext::InitChart( 1154 const OUString & rChartTypeServiceName // currently the old service name 1155 ) 1156 { 1157 uno::Reference< chart::XChartDocument > xDoc = mrImportHelper.GetChartDocument(); 1158 SAL_WARN_IF( !xDoc.is(), "xmloff.chart", "No valid document!" ); 1159 1160 // Remove Title and Diagram ("De-InitNew") 1161 uno::Reference< chart2::XChartDocument > xNewDoc( mrImportHelper.GetChartDocument(), uno::UNO_QUERY ); 1162 if( xNewDoc.is()) 1163 { 1164 xNewDoc->setFirstDiagram( nullptr ); 1165 uno::Reference< chart2::XTitled > xTitled( xNewDoc, uno::UNO_QUERY ); 1166 if( xTitled.is()) 1167 xTitled->setTitleObject( nullptr ); 1168 } 1169 1170 // Set the chart type via setting the diagram. 1171 if( !rChartTypeServiceName.isEmpty() && xDoc.is()) 1172 { 1173 uno::Reference< lang::XMultiServiceFactory > xFact( xDoc, uno::UNO_QUERY ); 1174 if( xFact.is()) 1175 { 1176 uno::Reference< chart::XDiagram > xDia( xFact->createInstance( rChartTypeServiceName ), uno::UNO_QUERY ); 1177 if( xDia.is()) 1178 xDoc->setDiagram( xDia ); 1179 } 1180 } 1181 } 1182 1183 SchXMLTitleContext::SchXMLTitleContext( SchXMLImportHelper& rImpHelper, SvXMLImport& rImport, 1184 OUString& rTitle, 1185 uno::Reference< drawing::XShape > xTitleShape ) : 1186 SvXMLImportContext( rImport ), 1187 mrImportHelper( rImpHelper ), 1188 mrTitle( rTitle ), 1189 mxTitleShape(std::move( xTitleShape )) 1190 { 1191 } 1192 1193 SchXMLTitleContext::~SchXMLTitleContext() 1194 {} 1195 1196 void SchXMLTitleContext::startFastElement( sal_Int32 /*nElement*/, 1197 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList ) 1198 { 1199 css::awt::Point aPosition; 1200 bool bHasXPosition=false; 1201 bool bHasYPosition=false; 1202 1203 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) ) 1204 { 1205 switch (aIter.getToken()) 1206 { 1207 case XML_ELEMENT(SVG, XML_X): 1208 case XML_ELEMENT(SVG_COMPAT, XML_X): 1209 { 1210 GetImport().GetMM100UnitConverter().convertMeasureToCore( 1211 aPosition.X, aIter.toView() ); 1212 bHasXPosition = true; 1213 break; 1214 } 1215 case XML_ELEMENT(SVG, XML_Y): 1216 case XML_ELEMENT(SVG_COMPAT, XML_Y): 1217 { 1218 GetImport().GetMM100UnitConverter().convertMeasureToCore( 1219 aPosition.Y, aIter.toView() ); 1220 bHasYPosition = true; 1221 break; 1222 } 1223 case XML_ELEMENT(CHART, XML_STYLE_NAME): 1224 msAutoStyleName = aIter.toString(); 1225 break; 1226 default: 1227 XMLOFF_WARN_UNKNOWN("xmloff", aIter); 1228 } 1229 } 1230 1231 if( mxTitleShape.is()) 1232 { 1233 if( bHasXPosition && bHasYPosition ) 1234 mxTitleShape->setPosition( aPosition ); 1235 1236 uno::Reference<beans::XPropertySet> xProp(mxTitleShape, uno::UNO_QUERY); 1237 mrImportHelper.FillAutoStyle(msAutoStyleName, xProp); 1238 } 1239 } 1240 1241 css::uno::Reference< css::xml::sax::XFastContextHandler > SchXMLTitleContext::createFastChildContext( 1242 sal_Int32 nElement, 1243 const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) 1244 { 1245 SvXMLImportContext* pContext = nullptr; 1246 1247 if( nElement == XML_ELEMENT(TEXT, XML_P) || 1248 nElement == XML_ELEMENT(LO_EXT, XML_P) ) 1249 { 1250 pContext = new SchXMLParagraphContext( GetImport(), mrTitle ); 1251 } 1252 else 1253 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement); 1254 1255 return pContext; 1256 } 1257 1258 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 1259
