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 <oox/token/namespaces.hxx> 21 #include <oox/token/tokens.hxx> 22 #include <oox/core/xmlfilterbase.hxx> 23 #include <oox/export/chartexport.hxx> 24 #include <oox/token/relationship.hxx> 25 #include <oox/export/utils.hxx> 26 #include <drawingml/chart/typegroupconverter.hxx> 27 28 #include <cstdio> 29 #include <iterator> 30 31 #include <com/sun/star/awt/Gradient.hpp> 32 #include <com/sun/star/chart/XChartDocument.hpp> 33 #include <com/sun/star/chart/ChartLegendPosition.hpp> 34 #include <com/sun/star/chart/XTwoAxisXSupplier.hpp> 35 #include <com/sun/star/chart/XTwoAxisYSupplier.hpp> 36 #include <com/sun/star/chart/XAxisZSupplier.hpp> 37 #include <com/sun/star/chart/XChartDataArray.hpp> 38 #include <com/sun/star/chart/ChartDataRowSource.hpp> 39 #include <com/sun/star/chart/ChartAxisAssign.hpp> 40 #include <com/sun/star/chart/ChartSeriesAddress.hpp> 41 #include <com/sun/star/chart/X3DDisplay.hpp> 42 #include <com/sun/star/chart/XStatisticDisplay.hpp> 43 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp> 44 #include <com/sun/star/chart/ChartSymbolType.hpp> 45 #include <com/sun/star/chart/ChartAxisMarks.hpp> 46 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp> 47 #include <com/sun/star/chart/ChartAxisPosition.hpp> 48 #include <com/sun/star/chart/ChartSolidType.hpp> 49 #include <com/sun/star/chart/DataLabelPlacement.hpp> 50 #include <com/sun/star/chart/ErrorBarStyle.hpp> 51 #include <com/sun/star/chart/MissingValueTreatment.hpp> 52 #include <com/sun/star/chart/XDiagramPositioning.hpp> 53 54 #include <com/sun/star/chart2/RelativePosition.hpp> 55 #include <com/sun/star/chart2/RelativeSize.hpp> 56 #include <com/sun/star/chart2/XChartDocument.hpp> 57 #include <com/sun/star/chart2/XDiagram.hpp> 58 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> 59 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp> 60 #include <com/sun/star/chart2/XChartTypeContainer.hpp> 61 #include <com/sun/star/chart2/XDataSeriesContainer.hpp> 62 #include <com/sun/star/chart2/DataPointGeometry3D.hpp> 63 #include <com/sun/star/chart2/DataPointLabel.hpp> 64 #include <com/sun/star/chart2/DataPointCustomLabelField.hpp> 65 #include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp> 66 #include <com/sun/star/chart2/Symbol.hpp> 67 #include <com/sun/star/chart2/data/XDataSource.hpp> 68 #include <com/sun/star/chart2/data/XDataSink.hpp> 69 #include <com/sun/star/chart2/data/XDataReceiver.hpp> 70 #include <com/sun/star/chart2/data/XDataProvider.hpp> 71 #include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp> 72 #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp> 73 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp> 74 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp> 75 76 #include <com/sun/star/beans/XPropertySet.hpp> 77 #include <com/sun/star/drawing/XShape.hpp> 78 #include <com/sun/star/drawing/FillStyle.hpp> 79 #include <com/sun/star/drawing/LineStyle.hpp> 80 #include <com/sun/star/drawing/BitmapMode.hpp> 81 #include <com/sun/star/awt/XBitmap.hpp> 82 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 83 #include <com/sun/star/lang/XServiceName.hpp> 84 85 #include <com/sun/star/table/CellAddress.hpp> 86 #include <com/sun/star/sheet/XFormulaParser.hpp> 87 #include <com/sun/star/sheet/FormulaToken.hpp> 88 #include <com/sun/star/sheet/AddressConvention.hpp> 89 90 #include <com/sun/star/text/WritingMode.hpp> 91 #include <com/sun/star/container/XNamed.hpp> 92 #include <com/sun/star/embed/XVisualObject.hpp> 93 #include <com/sun/star/embed/Aspects.hpp> 94 95 #include <comphelper/processfactory.hxx> 96 #include <comphelper/random.hxx> 97 #include <utility> 98 #include <xmloff/SchXMLSeriesHelper.hxx> 99 #include "ColorPropertySet.hxx" 100 101 #include <svl/zforlist.hxx> 102 #include <svl/numuno.hxx> 103 #include <tools/diagnose_ex.h> 104 #include <sal/log.hxx> 105 106 #include <set> 107 #include <unordered_set> 108 109 #include <rtl/math.hxx> 110 #include <o3tl/temporary.hxx> 111 112 using namespace css; 113 using namespace css::uno; 114 using namespace css::drawing; 115 using namespace ::oox::core; 116 using css::beans::PropertyValue; 117 using css::beans::XPropertySet; 118 using css::container::XNamed; 119 using css::table::CellAddress; 120 using css::sheet::XFormulaParser; 121 using ::oox::core::XmlFilterBase; 122 using ::sax_fastparser::FSHelperPtr; 123 124 namespace cssc = css::chart; 125 126 namespace oox { namespace drawingml { 127 128 namespace { 129 130 bool isPrimaryAxes(sal_Int32 nIndex) 131 { 132 assert(nIndex == 0 || nIndex == 1); 133 return nIndex != 1; 134 } 135 136 } 137 138 class lcl_MatchesRole 139 { 140 public: 141 explicit lcl_MatchesRole( const OUString & aRole ) : 142 m_aRole( aRole ) 143 {} 144 145 bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const 146 { 147 if( !xSeq.is() ) 148 return false; 149 Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY ); 150 OUString aRole; 151 152 return ( xProp.is() && 153 (xProp->getPropertyValue( "Role" ) >>= aRole ) && 154 m_aRole == aRole ); 155 } 156 157 private: 158 OUString const m_aRole; 159 }; 160 161 static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( const Reference< chart2::XDiagram > & xDiagram ) 162 { 163 Reference< chart2::data::XLabeledDataSequence > xResult; 164 try 165 { 166 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( 167 xDiagram, uno::UNO_QUERY_THROW ); 168 Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( 169 xCooSysCnt->getCoordinateSystems()); 170 for( sal_Int32 i=0; i<aCooSysSeq.getLength(); ++i ) 171 { 172 Reference< chart2::XCoordinateSystem > xCooSys( aCooSysSeq[i] ); 173 OSL_ASSERT( xCooSys.is()); 174 for( sal_Int32 nN = xCooSys->getDimension(); nN--; ) 175 { 176 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN); 177 for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI) 178 { 179 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI ); 180 OSL_ASSERT( xAxis.is()); 181 if( xAxis.is()) 182 { 183 chart2::ScaleData aScaleData = xAxis->getScaleData(); 184 if( aScaleData.Categories.is()) 185 { 186 xResult.set( aScaleData.Categories ); 187 break; 188 } 189 } 190 } 191 } 192 } 193 } 194 catch( const uno::Exception & ) 195 { 196 DBG_UNHANDLED_EXCEPTION("oox"); 197 } 198 199 return xResult; 200 } 201 202 static Reference< chart2::data::XLabeledDataSequence > 203 lcl_getDataSequenceByRole( 204 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq, 205 const OUString & rRole ) 206 { 207 Reference< chart2::data::XLabeledDataSequence > aNoResult; 208 209 const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray(); 210 const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength(); 211 const Reference< chart2::data::XLabeledDataSequence > * pMatch = 212 ::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole )); 213 214 if( pMatch != pEnd ) 215 return *pMatch; 216 217 return aNoResult; 218 } 219 220 static bool lcl_hasCategoryLabels( const Reference< chart2::XChartDocument >& xChartDoc ) 221 { 222 //categories are always the first sequence 223 Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram()); 224 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram ) ); 225 return xCategories.is(); 226 } 227 228 static bool lcl_isSeriesAttachedToFirstAxis( 229 const Reference< chart2::XDataSeries > & xDataSeries ) 230 { 231 bool bResult=true; 232 233 try 234 { 235 sal_Int32 nAxisIndex = 0; 236 Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW ); 237 xProp->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex; 238 bResult = (0==nAxisIndex); 239 } 240 catch( const uno::Exception & ) 241 { 242 DBG_UNHANDLED_EXCEPTION("oox"); 243 } 244 245 return bResult; 246 } 247 248 static OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence ) 249 { 250 OUStringBuffer aResult; 251 bool bPrecedeWithSpace = false; 252 for( sal_Int32 nIndex=0; nIndex<rSequence.getLength(); ++nIndex ) 253 { 254 if( !rSequence[nIndex].isEmpty()) 255 { 256 if( bPrecedeWithSpace ) 257 aResult.append( ' ' ); 258 aResult.append( rSequence[nIndex] ); 259 bPrecedeWithSpace = true; 260 } 261 } 262 return aResult.makeStringAndClear(); 263 } 264 265 static OUString lcl_getLabelString( const Reference< chart2::data::XDataSequence > & xLabelSeq ) 266 { 267 Sequence< OUString > aLabels; 268 269 uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY ); 270 if( xTextualDataSequence.is()) 271 { 272 aLabels = xTextualDataSequence->getTextualData(); 273 } 274 else if( xLabelSeq.is()) 275 { 276 Sequence< uno::Any > aAnies( xLabelSeq->getData()); 277 aLabels.realloc( aAnies.getLength()); 278 for( sal_Int32 i=0; i<aAnies.getLength(); ++i ) 279 aAnies[i] >>= aLabels[i]; 280 } 281 282 return lcl_flattenStringSequence( aLabels ); 283 } 284 285 static void lcl_fillCategoriesIntoStringVector( 286 const Reference< chart2::data::XDataSequence > & xCategories, 287 ::std::vector< OUString > & rOutCategories ) 288 { 289 OSL_ASSERT( xCategories.is()); 290 if( !xCategories.is()) 291 return; 292 Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xCategories, uno::UNO_QUERY ); 293 if( xTextualDataSequence.is()) 294 { 295 rOutCategories.clear(); 296 Sequence< OUString > aTextData( xTextualDataSequence->getTextualData()); 297 ::std::copy( aTextData.begin(), aTextData.end(), 298 ::std::back_inserter( rOutCategories )); 299 } 300 else 301 { 302 Sequence< uno::Any > aAnies( xCategories->getData()); 303 rOutCategories.resize( aAnies.getLength()); 304 for( sal_Int32 i=0; i<aAnies.getLength(); ++i ) 305 aAnies[i] >>= rOutCategories[i]; 306 } 307 } 308 309 static ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq ) 310 { 311 double fNan = 0.0; 312 ::rtl::math::setNan( &fNan ); 313 ::std::vector< double > aResult; 314 315 Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY ); 316 if( xNumSeq.is()) 317 { 318 Sequence< double > aValues( xNumSeq->getNumericalData()); 319 ::std::copy( aValues.begin(), aValues.end(), 320 ::std::back_inserter( aResult )); 321 } 322 else if( xSeq.is()) 323 { 324 Sequence< uno::Any > aAnies( xSeq->getData()); 325 aResult.resize( aAnies.getLength(), fNan ); 326 for( sal_Int32 i=0; i<aAnies.getLength(); ++i ) 327 aAnies[i] >>= aResult[i]; 328 } 329 return aResult; 330 } 331 332 static sal_Int32 lcl_getChartType( const OUString& sChartType ) 333 { 334 chart::TypeId eChartTypeId = chart::TYPEID_UNKNOWN; 335 if( sChartType == "com.sun.star.chart.BarDiagram" 336 || sChartType == "com.sun.star.chart2.ColumnChartType" ) 337 eChartTypeId = chart::TYPEID_BAR; 338 else if( sChartType == "com.sun.star.chart.AreaDiagram" 339 || sChartType == "com.sun.star.chart2.AreaChartType" ) 340 eChartTypeId = chart::TYPEID_AREA; 341 else if( sChartType == "com.sun.star.chart.LineDiagram" 342 || sChartType == "com.sun.star.chart2.LineChartType" ) 343 eChartTypeId = chart::TYPEID_LINE; 344 else if( sChartType == "com.sun.star.chart.PieDiagram" 345 || sChartType == "com.sun.star.chart2.PieChartType" ) 346 eChartTypeId = chart::TYPEID_PIE; 347 else if( sChartType == "com.sun.star.chart.DonutDiagram" 348 || sChartType == "com.sun.star.chart2.DonutChartType" ) 349 eChartTypeId = chart::TYPEID_DOUGHNUT; 350 else if( sChartType == "com.sun.star.chart.XYDiagram" 351 || sChartType == "com.sun.star.chart2.ScatterChartType" ) 352 eChartTypeId = chart::TYPEID_SCATTER; 353 else if( sChartType == "com.sun.star.chart.NetDiagram" 354 || sChartType == "com.sun.star.chart2.NetChartType" ) 355 eChartTypeId = chart::TYPEID_RADARLINE; 356 else if( sChartType == "com.sun.star.chart.FilledNetDiagram" 357 || sChartType == "com.sun.star.chart2.FilledNetChartType" ) 358 eChartTypeId = chart::TYPEID_RADARAREA; 359 else if( sChartType == "com.sun.star.chart.StockDiagram" 360 || sChartType == "com.sun.star.chart2.CandleStickChartType" ) 361 eChartTypeId = chart::TYPEID_STOCK; 362 else if( sChartType == "com.sun.star.chart.BubbleDiagram" 363 || sChartType == "com.sun.star.chart2.BubbleChartType" ) 364 eChartTypeId = chart::TYPEID_BUBBLE; 365 366 return eChartTypeId; 367 } 368 369 static sal_Int32 lcl_generateRandomValue() 370 { 371 return comphelper::rng::uniform_int_distribution(0, 100000000-1); 372 } 373 374 ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType ) 375 : DrawingML( std::move(pFS), pFB, eDocumentType ) 376 , mnXmlNamespace( nXmlNamespace ) 377 , mnSeriesCount(0) 378 , mxChartModel( xModel ) 379 , mpURLTransformer(new URLTransformer) 380 , mbHasCategoryLabels( false ) 381 , mbHasZAxis( false ) 382 , mbIs3DChart( false ) 383 , mbStacked(false) 384 , mbPercent(false) 385 { 386 } 387 388 void ChartExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer) 389 { 390 mpURLTransformer = pTransformer; 391 } 392 393 sal_Int32 ChartExport::getChartType( ) 394 { 395 OUString sChartType = mxDiagram->getDiagramType(); 396 return lcl_getChartType( sChartType ); 397 } 398 399 OUString ChartExport::parseFormula( const OUString& rRange ) 400 { 401 OUString aResult; 402 Reference< XFormulaParser > xParser; 403 uno::Reference< lang::XMultiServiceFactory > xSF( GetFB()->getModelFactory(), uno::UNO_QUERY ); 404 if( xSF.is() ) 405 { 406 try 407 { 408 xParser.set( xSF->createInstance("com.sun.star.sheet.FormulaParser"), UNO_QUERY ); 409 } 410 catch( Exception& ) 411 { 412 } 413 } 414 415 SAL_WARN_IF(!xParser.is(), "oox", "creating formula parser failed"); 416 417 if( xParser.is() ) 418 { 419 Reference< XPropertySet > xParserProps( xParser, uno::UNO_QUERY ); 420 // rRange is the result of a 421 // css::chart2::data::XDataSequence::getSourceRangeRepresentation() 422 // call that returns the range in the document's current UI notation. 423 // Creating a FormulaParser defaults to the same notation, for 424 // parseFormula() do not attempt to override the FormulaConvention 425 // property with css::sheet::AddressConvention::OOO or some such. 426 /* TODO: it would be much better to introduce a 427 * getSourceRangeRepresentation(css::sheet::AddressConvention) to 428 * return the ranges in a specific convention than converting them with 429 * the overhead of creating an XFormulaParser for each.. */ 430 uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( rRange, CellAddress( 0, 0, 0 ) ); 431 if( xParserProps.is() ) 432 { 433 xParserProps->setPropertyValue("FormulaConvention", uno::makeAny(css::sheet::AddressConvention::XL_OOX) ); 434 } 435 aResult = xParser->printFormula( aTokens, CellAddress( 0, 0, 0 ) ); 436 } 437 else 438 { 439 //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1 440 OUString aRange( rRange ); 441 if( aRange.startsWith("$") ) 442 aRange = aRange.copy(1); 443 aRange = aRange.replaceAll(".$", "!$" ); 444 aResult = aRange; 445 } 446 447 return aResult; 448 } 449 450 void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount ) 451 { 452 FSHelperPtr pFS = GetFS(); 453 454 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY ); 455 456 pFS->startElementNS(mnXmlNamespace, XML_graphicFrame); 457 458 pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr); 459 460 // TODO: get the correct chart name chart id 461 OUString sName = "Object 1"; 462 Reference< XNamed > xNamed( xShape, UNO_QUERY ); 463 if (xNamed.is()) 464 sName = xNamed->getName(); 465 466 pFS->startElementNS( mnXmlNamespace, XML_cNvPr, 467 XML_id, OString::number(nID), 468 XML_name, sName.toUtf8()); 469 470 OUString sURL; 471 if ( GetProperty( xShapeProps, "URL" ) ) 472 mAny >>= sURL; 473 if( !sURL.isEmpty() ) 474 { 475 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(), 476 oox::getRelationship(Relationship::HYPERLINK), 477 mpURLTransformer->getTransformedString(sURL), 478 mpURLTransformer->isExternalURL(sURL)); 479 480 mpFS->singleElementNS( XML_a, XML_hlinkClick, 481 FSNS( XML_r,XML_id ), sRelId.toUtf8() ); 482 } 483 pFS->endElementNS(mnXmlNamespace, XML_cNvPr); 484 485 pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr); 486 487 if( GetDocumentType() == DOCUMENT_PPTX ) 488 pFS->singleElementNS(mnXmlNamespace, XML_nvPr); 489 pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr ); 490 491 // visual chart properties 492 WriteShapeTransformation( xShape, mnXmlNamespace ); 493 494 // writer chart object 495 pFS->startElement(FSNS(XML_a, XML_graphic)); 496 pFS->startElement( FSNS( XML_a, XML_graphicData ), 497 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" ); 498 OUString sId; 499 const char* sFullPath = nullptr; 500 const char* sRelativePath = nullptr; 501 switch( GetDocumentType() ) 502 { 503 case DOCUMENT_DOCX: 504 { 505 sFullPath = "word/charts/chart"; 506 sRelativePath = "charts/chart"; 507 break; 508 } 509 case DOCUMENT_PPTX: 510 { 511 sFullPath = "ppt/charts/chart"; 512 sRelativePath = "../charts/chart"; 513 break; 514 } 515 case DOCUMENT_XLSX: 516 { 517 sFullPath = "xl/charts/chart"; 518 sRelativePath = "../charts/chart"; 519 break; 520 } 521 default: 522 { 523 sFullPath = "charts/chart"; 524 sRelativePath = "charts/chart"; 525 break; 526 } 527 } 528 OUString sFullStream = OUStringBuffer() 529 .appendAscii(sFullPath) 530 .append(nChartCount) 531 .append( ".xml" ) 532 .makeStringAndClear(); 533 OUString sRelativeStream = OUStringBuffer() 534 .appendAscii(sRelativePath) 535 .append(nChartCount) 536 .append( ".xml" ) 537 .makeStringAndClear(); 538 FSHelperPtr pChart = CreateOutputStream( 539 sFullStream, 540 sRelativeStream, 541 pFS->getOutputStream(), 542 "application/vnd.openxmlformats-officedocument.drawingml.chart+xml", 543 OUStringToOString(oox::getRelationship(Relationship::CHART), RTL_TEXTENCODING_UTF8).getStr(), 544 &sId ); 545 546 XmlFilterBase* pFB = GetFB(); 547 pFS->singleElement( FSNS( XML_c, XML_chart ), 548 FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)).toUtf8(), 549 FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)).toUtf8(), 550 FSNS(XML_r, XML_id), sId.toUtf8() ); 551 552 pFS->endElement( FSNS( XML_a, XML_graphicData ) ); 553 pFS->endElement( FSNS( XML_a, XML_graphic ) ); 554 pFS->endElementNS( mnXmlNamespace, XML_graphicFrame ); 555 556 SetFS( pChart ); 557 ExportContent(); 558 } 559 560 void ChartExport::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc ) 561 { 562 if( xChartDoc.is()) 563 try 564 { 565 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() ); 566 OSL_ENSURE( xDataProvider.is(), "No DataProvider" ); 567 if( xDataProvider.is()) 568 { 569 mbHasCategoryLabels = lcl_hasCategoryLabels( xChartDoc ); 570 } 571 } 572 catch( const uno::Exception & ) 573 { 574 DBG_UNHANDLED_EXCEPTION("oox"); 575 } 576 } 577 578 void ChartExport::ExportContent() 579 { 580 Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY ); 581 OSL_ASSERT( xChartDoc.is() ); 582 if( !xChartDoc.is() ) 583 return; 584 InitRangeSegmentationProperties( xChartDoc ); 585 // TODO: export chart 586 ExportContent_( ); 587 } 588 589 void ChartExport::ExportContent_() 590 { 591 Reference< css::chart::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY ); 592 if( xChartDoc.is()) 593 { 594 // determine if data comes from the outside 595 bool bIncludeTable = true; 596 597 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY ); 598 if( xNewDoc.is()) 599 { 600 // check if we have own data. If so we must not export the complete 601 // range string, as this is our only indicator for having own or 602 // external data. @todo: fix this in the file format! 603 Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY ); 604 if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" )) 605 { 606 bIncludeTable = false; 607 } 608 } 609 exportChartSpace( xChartDoc, bIncludeTable ); 610 } 611 else 612 { 613 OSL_FAIL( "Couldn't export chart due to wrong XModel" ); 614 } 615 } 616 617 void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument >& xChartDoc, 618 bool bIncludeTable ) 619 { 620 FSHelperPtr pFS = GetFS(); 621 XmlFilterBase* pFB = GetFB(); 622 pFS->startElement( FSNS( XML_c, XML_chartSpace ), 623 FSNS( XML_xmlns, XML_c ), pFB->getNamespaceURL(OOX_NS(dmlChart)).toUtf8(), 624 FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)).toUtf8(), 625 FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)).toUtf8()); 626 // TODO: get the correct editing language 627 pFS->singleElement(FSNS(XML_c, XML_lang), XML_val, "en-US"); 628 629 pFS->singleElement(FSNS(XML_c, XML_roundedCorners), XML_val, "0"); 630 631 if( !bIncludeTable ) 632 { 633 // TODO:external data 634 } 635 //XML_chart 636 exportChart(xChartDoc); 637 638 // TODO: printSettings 639 // TODO: style 640 // TODO: text properties 641 // TODO: shape properties 642 Reference< XPropertySet > xPropSet( xChartDoc->getArea(), uno::UNO_QUERY ); 643 if( xPropSet.is() ) 644 exportShapeProps( xPropSet ); 645 646 //XML_externalData 647 exportExternalData(xChartDoc); 648 649 pFS->endElement( FSNS( XML_c, XML_chartSpace ) ); 650 } 651 652 void ChartExport::exportExternalData( const Reference< css::chart::XChartDocument >& xChartDoc ) 653 { 654 // Embedded external data is grab bagged for docx file hence adding export part of 655 // external data for docx files only. 656 if(GetDocumentType() != DOCUMENT_DOCX) 657 return; 658 659 OUString externalDataPath; 660 Reference< beans::XPropertySet > xDocPropSet( xChartDoc->getDiagram(), uno::UNO_QUERY ); 661 if( xDocPropSet.is()) 662 { 663 try 664 { 665 Any aAny( xDocPropSet->getPropertyValue( "ExternalData" )); 666 aAny >>= externalDataPath; 667 } 668 catch( beans::UnknownPropertyException & ) 669 { 670 SAL_WARN("oox", "Required property not found in ChartDocument"); 671 } 672 } 673 if(!externalDataPath.isEmpty()) 674 { 675 // Here adding external data entry to relationship. 676 OUString relationPath = externalDataPath; 677 // Converting absolute path to relative path. 678 if( externalDataPath[ 0 ] != '.' && externalDataPath[ 1 ] != '.') 679 { 680 sal_Int32 nSepPos = externalDataPath.indexOf( '/', 0 ); 681 if( nSepPos > 0) 682 { 683 relationPath = relationPath.copy( nSepPos, ::std::max< sal_Int32 >( externalDataPath.getLength(), 0 ) - nSepPos ); 684 relationPath = ".." + relationPath; 685 } 686 } 687 FSHelperPtr pFS = GetFS(); 688 OUString type = oox::getRelationship(Relationship::PACKAGE); 689 if (relationPath.endsWith(".bin")) 690 type = oox::getRelationship(Relationship::OLEOBJECT); 691 692 OUString sRelId = GetFB()->addRelation(pFS->getOutputStream(), 693 type, 694 relationPath); 695 pFS->singleElementNS(XML_c, XML_externalData, FSNS(XML_r, XML_id), sRelId.toUtf8()); 696 } 697 } 698 699 void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xChartDoc ) 700 { 701 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY ); 702 mxDiagram.set( xChartDoc->getDiagram() ); 703 if( xNewDoc.is()) 704 mxNewDiagram.set( xNewDoc->getFirstDiagram()); 705 706 // get Properties of ChartDocument 707 bool bHasMainTitle = false; 708 bool bHasLegend = false; 709 Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY ); 710 if( xDocPropSet.is()) 711 { 712 try 713 { 714 bool bHasSubTitle = false; 715 Any aAny( xDocPropSet->getPropertyValue("HasMainTitle")); 716 aAny >>= bHasMainTitle; 717 aAny = xDocPropSet->getPropertyValue("HasSubTitle"); 718 aAny >>= bHasSubTitle; 719 aAny = xDocPropSet->getPropertyValue("HasLegend"); 720 aAny >>= bHasLegend; 721 } 722 catch( beans::UnknownPropertyException & ) 723 { 724 SAL_WARN("oox", "Required property not found in ChartDocument"); 725 } 726 } // if( xDocPropSet.is()) 727 728 // chart element 729 730 FSHelperPtr pFS = GetFS(); 731 pFS->startElement(FSNS(XML_c, XML_chart)); 732 733 // title 734 if( bHasMainTitle ) 735 { 736 Reference< drawing::XShape > xShape = xChartDoc->getTitle(); 737 if( xShape.is() ) 738 { 739 exportTitle( xShape ); 740 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0"); 741 } 742 } 743 InitPlotArea( ); 744 if( mbIs3DChart ) 745 { 746 exportView3D(); 747 748 // floor 749 Reference< beans::XPropertySet > xFloor( mxNewDiagram->getFloor(), uno::UNO_QUERY ); 750 if( xFloor.is() ) 751 { 752 pFS->startElement(FSNS(XML_c, XML_floor)); 753 exportShapeProps( xFloor ); 754 pFS->endElement( FSNS( XML_c, XML_floor ) ); 755 } 756 757 // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color). 758 // It is controlled by the same Wall property. 759 Reference< beans::XPropertySet > xWall( mxNewDiagram->getWall(), uno::UNO_QUERY ); 760 if( xWall.is() ) 761 { 762 // sideWall 763 pFS->startElement(FSNS(XML_c, XML_sideWall)); 764 exportShapeProps( xWall ); 765 pFS->endElement( FSNS( XML_c, XML_sideWall ) ); 766 767 // backWall 768 pFS->startElement(FSNS(XML_c, XML_backWall)); 769 exportShapeProps( xWall ); 770 pFS->endElement( FSNS( XML_c, XML_backWall ) ); 771 } 772 773 } 774 // plot area 775 exportPlotArea( xChartDoc ); 776 // legend 777 if( bHasLegend ) 778 exportLegend( xChartDoc ); 779 780 uno::Reference<beans::XPropertySet> xDiagramPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY); 781 uno::Any aPlotVisOnly = xDiagramPropSet->getPropertyValue("IncludeHiddenCells"); 782 bool bIncludeHiddenCells = false; 783 aPlotVisOnly >>= bIncludeHiddenCells; 784 pFS->singleElement(FSNS(XML_c, XML_plotVisOnly), XML_val, ToPsz10(!bIncludeHiddenCells)); 785 786 exportMissingValueTreatment(Reference<beans::XPropertySet>(mxDiagram, uno::UNO_QUERY)); 787 788 pFS->endElement( FSNS( XML_c, XML_chart ) ); 789 } 790 791 void ChartExport::exportMissingValueTreatment(const uno::Reference<beans::XPropertySet>& xPropSet) 792 { 793 if (!xPropSet.is()) 794 return; 795 796 sal_Int32 nVal = 0; 797 uno::Any aAny = xPropSet->getPropertyValue("MissingValueTreatment"); 798 if (!(aAny >>= nVal)) 799 return; 800 801 const char* pVal = nullptr; 802 switch (nVal) 803 { 804 case cssc::MissingValueTreatment::LEAVE_GAP: 805 pVal = "gap"; 806 break; 807 case cssc::MissingValueTreatment::USE_ZERO: 808 pVal = "zero"; 809 break; 810 case cssc::MissingValueTreatment::CONTINUE: 811 pVal = "span"; 812 break; 813 default: 814 SAL_WARN("oox", "unknown MissingValueTreatment value"); 815 break; 816 } 817 818 FSHelperPtr pFS = GetFS(); 819 pFS->singleElement(FSNS(XML_c, XML_dispBlanksAs), XML_val, pVal); 820 } 821 822 void ChartExport::exportLegend( const Reference< css::chart::XChartDocument >& xChartDoc ) 823 { 824 FSHelperPtr pFS = GetFS(); 825 pFS->startElement(FSNS(XML_c, XML_legend)); 826 827 Reference< beans::XPropertySet > xProp( xChartDoc->getLegend(), uno::UNO_QUERY ); 828 if( xProp.is() ) 829 { 830 // position 831 css::chart::ChartLegendPosition aLegendPos = css::chart::ChartLegendPosition_NONE; 832 try 833 { 834 Any aAny( xProp->getPropertyValue( "Alignment" )); 835 aAny >>= aLegendPos; 836 } 837 catch( beans::UnknownPropertyException & ) 838 { 839 SAL_WARN("oox", "Property Align not found in ChartLegend"); 840 } 841 842 const char* strPos = nullptr; 843 switch( aLegendPos ) 844 { 845 case css::chart::ChartLegendPosition_LEFT: 846 strPos = "l"; 847 break; 848 case css::chart::ChartLegendPosition_RIGHT: 849 strPos = "r"; 850 break; 851 case css::chart::ChartLegendPosition_TOP: 852 strPos = "t"; 853 break; 854 case css::chart::ChartLegendPosition_BOTTOM: 855 strPos = "b"; 856 break; 857 case css::chart::ChartLegendPosition_NONE: 858 case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE: 859 // nothing 860 break; 861 } 862 863 if( strPos != nullptr ) 864 { 865 pFS->singleElement(FSNS(XML_c, XML_legendPos), XML_val, strPos); 866 } 867 868 uno::Any aRelativePos = xProp->getPropertyValue("RelativePosition"); 869 if (aRelativePos.hasValue()) 870 { 871 pFS->startElement(FSNS(XML_c, XML_layout)); 872 pFS->startElement(FSNS(XML_c, XML_manualLayout)); 873 874 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge"); 875 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge"); 876 chart2::RelativePosition aPos = aRelativePos.get<chart2::RelativePosition>(); 877 878 const double x = aPos.Primary; 879 const double y = aPos.Secondary; 880 881 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x)); 882 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y)); 883 884 uno::Any aRelativeSize = xProp->getPropertyValue("RelativeSize"); 885 if (aRelativeSize.hasValue()) 886 { 887 chart2::RelativeSize aSize = aRelativeSize.get<chart2::RelativeSize>(); 888 889 const double w = aSize.Primary; 890 const double h = aSize.Secondary; 891 892 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w)); 893 894 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h)); 895 } 896 897 SAL_WARN_IF(aPos.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position"); 898 899 pFS->endElement(FSNS(XML_c, XML_manualLayout)); 900 pFS->endElement(FSNS(XML_c, XML_layout)); 901 } 902 903 if (strPos != nullptr) 904 { 905 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0"); 906 } 907 908 // shape properties 909 exportShapeProps( xProp ); 910 911 // draw-chart:txPr text properties 912 exportTextProps( xProp ); 913 } 914 915 // legendEntry 916 917 pFS->endElement( FSNS( XML_c, XML_legend ) ); 918 } 919 920 void ChartExport::exportTitle( const Reference< XShape >& xShape ) 921 { 922 OUString sText; 923 Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY ); 924 if( xPropSet.is()) 925 { 926 xPropSet->getPropertyValue("String") >>= sText; 927 } 928 if( sText.isEmpty() ) 929 return; 930 931 FSHelperPtr pFS = GetFS(); 932 pFS->startElement(FSNS(XML_c, XML_title)); 933 934 pFS->startElement(FSNS(XML_c, XML_tx)); 935 pFS->startElement(FSNS(XML_c, XML_rich)); 936 937 // TODO: bodyPr 938 const char* sWritingMode = nullptr; 939 bool bVertical = false; 940 xPropSet->getPropertyValue("StackedText") >>= bVertical; 941 if( bVertical ) 942 sWritingMode = "wordArtVert"; 943 944 sal_Int32 nRotation = 0; 945 xPropSet->getPropertyValue("TextRotation") >>= nRotation; 946 947 pFS->singleElement( FSNS( XML_a, XML_bodyPr ), 948 XML_vert, sWritingMode, 949 XML_rot, oox::drawingml::calcRotationValue(nRotation) ); 950 // TODO: lstStyle 951 pFS->singleElement(FSNS(XML_a, XML_lstStyle)); 952 // FIXME: handle multiple paragraphs to parse aText 953 pFS->startElement(FSNS(XML_a, XML_p)); 954 955 pFS->startElement(FSNS(XML_a, XML_pPr)); 956 957 bool bDummy = false; 958 sal_Int32 nDummy; 959 WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, nDummy ); 960 961 pFS->endElement( FSNS( XML_a, XML_pPr ) ); 962 963 pFS->startElement(FSNS(XML_a, XML_r)); 964 bDummy = false; 965 WriteRunProperties( xPropSet, false, XML_rPr, true, bDummy, nDummy ); 966 pFS->startElement(FSNS(XML_a, XML_t)); 967 pFS->writeEscaped( sText ); 968 pFS->endElement( FSNS( XML_a, XML_t ) ); 969 pFS->endElement( FSNS( XML_a, XML_r ) ); 970 971 pFS->endElement( FSNS( XML_a, XML_p ) ); 972 973 pFS->endElement( FSNS( XML_c, XML_rich ) ); 974 pFS->endElement( FSNS( XML_c, XML_tx ) ); 975 976 uno::Any aManualLayout = xPropSet->getPropertyValue("RelativePosition"); 977 if (aManualLayout.hasValue()) 978 { 979 pFS->startElement(FSNS(XML_c, XML_layout)); 980 pFS->startElement(FSNS(XML_c, XML_manualLayout)); 981 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge"); 982 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge"); 983 984 Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY); 985 awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT); 986 987 // awt::Size aSize = xShape->getSize(); 988 awt::Point aPos2 = xShape->getPosition(); 989 double x = static_cast<double>(aPos2.X) / static_cast<double>(aPageSize.Width); 990 double y = static_cast<double>(aPos2.Y) / static_cast<double>(aPageSize.Height); 991 /* 992 pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge"); 993 pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge"); 994 */ 995 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x)); 996 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y)); 997 /* 998 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, ""); 999 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, ""); 1000 */ 1001 pFS->endElement(FSNS(XML_c, XML_manualLayout)); 1002 pFS->endElement(FSNS(XML_c, XML_layout)); 1003 } 1004 1005 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0"); 1006 1007 // shape properties 1008 if( xPropSet.is() ) 1009 { 1010 exportShapeProps( xPropSet ); 1011 } 1012 1013 pFS->endElement( FSNS( XML_c, XML_title ) ); 1014 } 1015 1016 void ChartExport::exportPlotArea( const Reference< css::chart::XChartDocument >& xChartDoc ) 1017 { 1018 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY ); 1019 if( ! xBCooSysCnt.is()) 1020 return; 1021 1022 // plot-area element 1023 1024 FSHelperPtr pFS = GetFS(); 1025 pFS->startElement(FSNS(XML_c, XML_plotArea)); 1026 1027 Reference<beans::XPropertySet> xWall(mxNewDiagram, uno::UNO_QUERY); 1028 if( xWall.is() ) 1029 { 1030 uno::Any aAny = xWall->getPropertyValue("RelativePosition"); 1031 if (aAny.hasValue()) 1032 { 1033 chart2::RelativePosition aPos = aAny.get<chart2::RelativePosition>(); 1034 aAny = xWall->getPropertyValue("RelativeSize"); 1035 chart2::RelativeSize aSize = aAny.get<chart2::RelativeSize>(); 1036 uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( xChartDoc->getDiagram(), uno::UNO_QUERY ); 1037 exportManualLayout(aPos, aSize, xDiagramPositioning->isExcludingDiagramPositioning() ); 1038 } 1039 } 1040 1041 // chart type 1042 Sequence< Reference< chart2::XCoordinateSystem > > 1043 aCooSysSeq( xBCooSysCnt->getCoordinateSystems()); 1044 for( sal_Int32 nCSIdx=0; nCSIdx<aCooSysSeq.getLength(); ++nCSIdx ) 1045 { 1046 1047 Reference< chart2::XChartTypeContainer > xCTCnt( aCooSysSeq[nCSIdx], uno::UNO_QUERY ); 1048 if( ! xCTCnt.is()) 1049 continue; 1050 mnSeriesCount=0; 1051 Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes()); 1052 for( sal_Int32 nCTIdx=0; nCTIdx<aCTSeq.getLength(); ++nCTIdx ) 1053 { 1054 Reference< chart2::XDataSeriesContainer > xDSCnt( aCTSeq[nCTIdx], uno::UNO_QUERY ); 1055 if( ! xDSCnt.is()) 1056 return; 1057 Reference< chart2::XChartType > xChartType( aCTSeq[nCTIdx], uno::UNO_QUERY ); 1058 if( ! xChartType.is()) 1059 continue; 1060 // note: if xDSCnt.is() then also aCTSeq[nCTIdx] 1061 OUString aChartType( xChartType->getChartType()); 1062 sal_Int32 eChartType = lcl_getChartType( aChartType ); 1063 switch( eChartType ) 1064 { 1065 case chart::TYPEID_BAR: 1066 { 1067 exportBarChart( xChartType ); 1068 break; 1069 } 1070 case chart::TYPEID_AREA: 1071 { 1072 exportAreaChart( xChartType ); 1073 break; 1074 } 1075 case chart::TYPEID_LINE: 1076 { 1077 exportLineChart( xChartType ); 1078 break; 1079 } 1080 case chart::TYPEID_BUBBLE: 1081 { 1082 exportBubbleChart( xChartType ); 1083 break; 1084 } 1085 case chart::TYPEID_OFPIE: 1086 { 1087 break; 1088 } 1089 case chart::TYPEID_DOUGHNUT: 1090 case chart::TYPEID_PIE: 1091 { 1092 exportPieChart( xChartType ); 1093 break; 1094 } 1095 case chart::TYPEID_RADARLINE: 1096 case chart::TYPEID_RADARAREA: 1097 { 1098 exportRadarChart( xChartType ); 1099 break; 1100 } 1101 case chart::TYPEID_SCATTER: 1102 { 1103 exportScatterChart( xChartType ); 1104 break; 1105 } 1106 case chart::TYPEID_STOCK: 1107 { 1108 exportStockChart( xChartType ); 1109 break; 1110 } 1111 case chart::TYPEID_SURFACE: 1112 { 1113 exportSurfaceChart( xChartType ); 1114 break; 1115 } 1116 default: 1117 { 1118 SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type"); 1119 break; 1120 } 1121 } 1122 1123 } 1124 } 1125 //Axis Data 1126 exportAxes( ); 1127 // Data Table 1128 exportDataTable(); 1129 1130 // shape properties 1131 /* 1132 * Export the Plot area Shape Properties 1133 * eg: Fill and Outline 1134 */ 1135 Reference< css::chart::X3DDisplay > xWallFloorSupplier( mxDiagram, uno::UNO_QUERY ); 1136 // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall. 1137 // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts. 1138 // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice. 1139 // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall. 1140 if( !mbIs3DChart && xWallFloorSupplier.is() ) 1141 { 1142 Reference< beans::XPropertySet > xWallPropSet( xWallFloorSupplier->getWall(), uno::UNO_QUERY ); 1143 if( xWallPropSet.is() ) 1144 { 1145 uno::Any aAny = xWallPropSet->getPropertyValue("LineStyle"); 1146 sal_Int32 eChartType = getChartType( ); 1147 // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice 1148 // make invisible the Wall shape properties, in case of these charts. Or in the future set 1149 // the default LineStyle of these charts to LineStyle_NONE. 1150 bool noSupportWallProp = ( (eChartType == chart::TYPEID_PIE) || (eChartType == chart::TYPEID_RADARLINE) || (eChartType == chart::TYPEID_RADARAREA) ); 1151 if ( noSupportWallProp && (aAny != drawing::LineStyle_NONE) ) 1152 { 1153 xWallPropSet->setPropertyValue( "LineStyle", uno::Any(drawing::LineStyle_NONE) ); 1154 } 1155 exportShapeProps( xWallPropSet ); 1156 } 1157 } 1158 1159 pFS->endElement( FSNS( XML_c, XML_plotArea ) ); 1160 1161 } 1162 1163 void ChartExport::exportManualLayout(const css::chart2::RelativePosition& rPos, 1164 const css::chart2::RelativeSize& rSize, 1165 const bool bIsExcludingDiagramPositioning) 1166 { 1167 FSHelperPtr pFS = GetFS(); 1168 pFS->startElement(FSNS(XML_c, XML_layout)); 1169 pFS->startElement(FSNS(XML_c, XML_manualLayout)); 1170 1171 // By default layoutTarget is set to "outer" and we shouldn't save it in that case 1172 if ( bIsExcludingDiagramPositioning ) 1173 { 1174 pFS->singleElement(FSNS(XML_c, XML_layoutTarget), XML_val, "inner"); 1175 } 1176 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge"); 1177 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge"); 1178 1179 double x = rPos.Primary; 1180 double y = rPos.Secondary; 1181 const double w = rSize.Primary; 1182 const double h = rSize.Secondary; 1183 switch (rPos.Anchor) 1184 { 1185 case drawing::Alignment_LEFT: 1186 y -= (h/2); 1187 break; 1188 case drawing::Alignment_TOP_LEFT: 1189 break; 1190 case drawing::Alignment_BOTTOM_LEFT: 1191 y -= h; 1192 break; 1193 case drawing::Alignment_TOP: 1194 x -= (w/2); 1195 break; 1196 case drawing::Alignment_CENTER: 1197 x -= (w/2); 1198 y -= (h/2); 1199 break; 1200 case drawing::Alignment_BOTTOM: 1201 x -= (w/2); 1202 y -= h; 1203 break; 1204 case drawing::Alignment_TOP_RIGHT: 1205 x -= w; 1206 break; 1207 case drawing::Alignment_BOTTOM_RIGHT: 1208 x -= w; 1209 y -= h; 1210 break; 1211 case drawing::Alignment_RIGHT: 1212 y -= (h/2); 1213 x -= w; 1214 break; 1215 default: 1216 SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16>(rPos.Anchor)); 1217 } 1218 1219 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x)); 1220 1221 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y)); 1222 1223 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w)); 1224 1225 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h)); 1226 1227 pFS->endElement(FSNS(XML_c, XML_manualLayout)); 1228 pFS->endElement(FSNS(XML_c, XML_layout)); 1229 } 1230 1231 void ChartExport::exportFill( const Reference< XPropertySet >& xPropSet ) 1232 { 1233 if ( !GetProperty( xPropSet, "FillStyle" ) ) 1234 return; 1235 FillStyle aFillStyle( FillStyle_NONE ); 1236 xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle; 1237 switch( aFillStyle ) 1238 { 1239 case FillStyle_GRADIENT : 1240 exportGradientFill( xPropSet ); 1241 break; 1242 case FillStyle_BITMAP : 1243 exportBitmapFill( xPropSet ); 1244 break; 1245 case FillStyle_HATCH: 1246 exportHatch(xPropSet); 1247 break; 1248 default: 1249 WriteFill( xPropSet ); 1250 } 1251 } 1252 1253 void ChartExport::exportHatch( const Reference< XPropertySet >& xPropSet ) 1254 { 1255 if (!xPropSet.is()) 1256 return; 1257 1258 if (GetProperty(xPropSet, "FillHatchName")) 1259 { 1260 OUString aHatchName; 1261 mAny >>= aHatchName; 1262 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY ); 1263 uno::Reference< container::XNameAccess > xHatchTable( xFact->createInstance("com.sun.star.drawing.HatchTable"), uno::UNO_QUERY ); 1264 uno::Any rValue = xHatchTable->getByName(aHatchName); 1265 css::drawing::Hatch aHatch; 1266 rValue >>= aHatch; 1267 WritePattFill(xPropSet, aHatch); 1268 } 1269 1270 } 1271 1272 void ChartExport::exportBitmapFill( const Reference< XPropertySet >& xPropSet ) 1273 { 1274 if( xPropSet.is() ) 1275 { 1276 OUString sFillBitmapName; 1277 xPropSet->getPropertyValue("FillBitmapName") >>= sFillBitmapName; 1278 1279 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY ); 1280 try 1281 { 1282 uno::Reference< container::XNameAccess > xBitmapTable( xFact->createInstance("com.sun.star.drawing.BitmapTable"), uno::UNO_QUERY ); 1283 uno::Any rValue = xBitmapTable->getByName( sFillBitmapName ); 1284 if (rValue.has<uno::Reference<awt::XBitmap>>()) 1285 { 1286 uno::Reference<awt::XBitmap> xBitmap = rValue.get<uno::Reference<awt::XBitmap>>(); 1287 uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY); 1288 if (xGraphic.is()) 1289 { 1290 WriteXGraphicBlipFill(xPropSet, xGraphic, XML_a, true, true); 1291 } 1292 } 1293 } 1294 catch (const uno::Exception &) 1295 { 1296 TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill"); 1297 } 1298 } 1299 } 1300 1301 void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet ) 1302 { 1303 if( xPropSet.is() ) 1304 { 1305 OUString sFillGradientName; 1306 xPropSet->getPropertyValue("FillGradientName") >>= sFillGradientName; 1307 1308 awt::Gradient aGradient; 1309 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY ); 1310 try 1311 { 1312 uno::Reference< container::XNameAccess > xGradient( xFact->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY ); 1313 uno::Any rValue = xGradient->getByName( sFillGradientName ); 1314 if( rValue >>= aGradient ) 1315 { 1316 mpFS->startElementNS(XML_a, XML_gradFill); 1317 WriteGradientFill( aGradient ); 1318 mpFS->endElementNS( XML_a, XML_gradFill ); 1319 } 1320 } 1321 catch (const uno::Exception &) 1322 { 1323 TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill"); 1324 } 1325 1326 } 1327 } 1328 1329 void ChartExport::exportDataTable( ) 1330 { 1331 FSHelperPtr pFS = GetFS(); 1332 Reference< beans::XPropertySet > aPropSet( mxDiagram, uno::UNO_QUERY ); 1333 1334 bool bShowVBorder = false; 1335 bool bShowHBorder = false; 1336 bool bShowOutline = false; 1337 1338 if (GetProperty( aPropSet, "DataTableHBorder")) 1339 mAny >>= bShowHBorder; 1340 if (GetProperty( aPropSet, "DataTableVBorder")) 1341 mAny >>= bShowVBorder; 1342 if (GetProperty( aPropSet, "DataTableOutline")) 1343 mAny >>= bShowOutline; 1344 1345 if (bShowVBorder || bShowHBorder || bShowOutline) 1346 { 1347 pFS->startElement(FSNS(XML_c, XML_dTable)); 1348 if (bShowHBorder) 1349 pFS->singleElement( FSNS( XML_c, XML_showHorzBorder ), 1350 XML_val, "1" ); 1351 if (bShowVBorder) 1352 pFS->singleElement(FSNS(XML_c, XML_showVertBorder), XML_val, "1"); 1353 if (bShowOutline) 1354 pFS->singleElement(FSNS(XML_c, XML_showOutline), XML_val, "1"); 1355 1356 pFS->endElement( FSNS( XML_c, XML_dTable)); 1357 } 1358 1359 } 1360 void ChartExport::exportAreaChart( const Reference< chart2::XChartType >& xChartType ) 1361 { 1362 FSHelperPtr pFS = GetFS(); 1363 sal_Int32 nTypeId = XML_areaChart; 1364 if( mbIs3DChart ) 1365 nTypeId = XML_area3DChart; 1366 pFS->startElement(FSNS(XML_c, nTypeId)); 1367 1368 exportGrouping( ); 1369 bool bPrimaryAxes = true; 1370 exportAllSeries(xChartType, bPrimaryAxes); 1371 exportAxesId(bPrimaryAxes); 1372 1373 pFS->endElement( FSNS( XML_c, nTypeId ) ); 1374 } 1375 1376 void ChartExport::exportBarChart( const Reference< chart2::XChartType >& xChartType ) 1377 { 1378 sal_Int32 nTypeId = XML_barChart; 1379 if( mbIs3DChart ) 1380 nTypeId = XML_bar3DChart; 1381 FSHelperPtr pFS = GetFS(); 1382 pFS->startElement(FSNS(XML_c, nTypeId)); 1383 // bar direction 1384 bool bVertical = false; 1385 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY); 1386 if( GetProperty( xPropSet, "Vertical" ) ) 1387 mAny >>= bVertical; 1388 1389 const char* bardir = bVertical? "bar":"col"; 1390 pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, bardir); 1391 1392 exportGrouping( true ); 1393 1394 exportVaryColors(xChartType); 1395 1396 bool bPrimaryAxes = true; 1397 exportAllSeries(xChartType, bPrimaryAxes); 1398 1399 Reference< XPropertySet > xTypeProp( xChartType, uno::UNO_QUERY ); 1400 1401 if( xTypeProp.is() && GetProperty( xTypeProp, "GapwidthSequence") ) 1402 { 1403 uno::Sequence< sal_Int32 > aBarPositionSequence; 1404 mAny >>= aBarPositionSequence; 1405 if( aBarPositionSequence.hasElements() ) 1406 { 1407 sal_Int32 nGapWidth = aBarPositionSequence[0]; 1408 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(nGapWidth)); 1409 } 1410 } 1411 1412 if( mbIs3DChart ) 1413 { 1414 // Shape 1415 namespace cssc = css::chart; 1416 sal_Int32 nGeom3d = cssc::ChartSolidType::RECTANGULAR_SOLID; 1417 if( xPropSet.is() && GetProperty( xPropSet, "SolidType") ) 1418 mAny >>= nGeom3d; 1419 const char* sShapeType = nullptr; 1420 switch( nGeom3d ) 1421 { 1422 case cssc::ChartSolidType::RECTANGULAR_SOLID: 1423 sShapeType = "box"; 1424 break; 1425 case cssc::ChartSolidType::CONE: 1426 sShapeType = "cone"; 1427 break; 1428 case cssc::ChartSolidType::CYLINDER: 1429 sShapeType = "cylinder"; 1430 break; 1431 case cssc::ChartSolidType::PYRAMID: 1432 sShapeType = "pyramid"; 1433 break; 1434 } 1435 pFS->singleElement(FSNS(XML_c, XML_shape), XML_val, sShapeType); 1436 } 1437 1438 //overlap 1439 if( !mbIs3DChart && xTypeProp.is() && GetProperty( xTypeProp, "OverlapSequence") ) 1440 { 1441 uno::Sequence< sal_Int32 > aBarPositionSequence; 1442 mAny >>= aBarPositionSequence; 1443 if( aBarPositionSequence.hasElements() ) 1444 { 1445 sal_Int32 nOverlap = aBarPositionSequence[0]; 1446 // Stacked/Percent Bar/Column chart Overlap-workaround 1447 // Export the Overlap value with 100% for stacked charts, 1448 // because the default overlap value of the Bar/Column chart is 0% and 1449 // LibreOffice do nothing with the overlap value in Stacked charts case, 1450 // unlike the MS Office, which is interpreted differently. 1451 if( ( mbStacked || mbPercent ) && nOverlap != 100 ) 1452 { 1453 nOverlap = 100; 1454 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap)); 1455 } 1456 else // Normal bar chart 1457 { 1458 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap)); 1459 } 1460 } 1461 } 1462 1463 exportAxesId(bPrimaryAxes); 1464 1465 pFS->endElement( FSNS( XML_c, nTypeId ) ); 1466 } 1467 1468 void ChartExport::exportBubbleChart( const Reference< chart2::XChartType >& xChartType ) 1469 { 1470 FSHelperPtr pFS = GetFS(); 1471 pFS->startElement(FSNS(XML_c, XML_bubbleChart)); 1472 1473 exportVaryColors(xChartType); 1474 1475 bool bPrimaryAxes = true; 1476 exportAllSeries(xChartType, bPrimaryAxes); 1477 1478 pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0"); 1479 1480 exportAxesId(bPrimaryAxes); 1481 1482 pFS->endElement( FSNS( XML_c, XML_bubbleChart ) ); 1483 } 1484 1485 void ChartExport::exportDoughnutChart( const Reference< chart2::XChartType >& xChartType ) 1486 { 1487 FSHelperPtr pFS = GetFS(); 1488 pFS->startElement(FSNS(XML_c, XML_doughnutChart)); 1489 1490 exportVaryColors(xChartType); 1491 1492 bool bPrimaryAxes = true; 1493 exportAllSeries(xChartType, bPrimaryAxes); 1494 // firstSliceAng 1495 exportFirstSliceAng( ); 1496 //FIXME: holeSize 1497 pFS->singleElement(FSNS(XML_c, XML_holeSize), XML_val, OString::number(50)); 1498 1499 pFS->endElement( FSNS( XML_c, XML_doughnutChart ) ); 1500 } 1501 1502 namespace { 1503 1504 std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(const Reference< chart2::XChartType >& xChartType) 1505 { 1506 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitSeries; 1507 std::map<sal_Int32, size_t> aMapAxisToIndex; 1508 1509 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY ); 1510 if(xDSCnt.is()) 1511 { 1512 sal_Int32 nAxisIndexOfFirstSeries = -1; 1513 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries()); 1514 for (sal_Int32 nIndex = 0, nEnd = aSeriesSeq.getLength(); nIndex < nEnd; ++nIndex) 1515 { 1516 uno::Reference<chart2::XDataSeries> xSeries = aSeriesSeq[nIndex]; 1517 Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY); 1518 if (!xPropSet.is()) 1519 continue; 1520 1521 sal_Int32 nAxisIndex = -1; 1522 uno::Any aAny = xPropSet->getPropertyValue("AttachedAxisIndex"); 1523 aAny >>= nAxisIndex; 1524 size_t nVectorPos = 0; 1525 if (nAxisIndexOfFirstSeries == -1) 1526 { 1527 nAxisIndexOfFirstSeries = nAxisIndex; 1528 } 1529 1530 auto it = aMapAxisToIndex.find(nAxisIndex); 1531 if (it == aMapAxisToIndex.end()) 1532 { 1533 aSplitSeries.emplace_back(); 1534 nVectorPos = aSplitSeries.size() - 1; 1535 aMapAxisToIndex.insert(std::pair<sal_Int32, size_t>(nAxisIndex, nVectorPos)); 1536 } 1537 else 1538 { 1539 nVectorPos = it->second; 1540 } 1541 1542 uno::Sequence<Reference<chart2::XDataSeries> >& rAxisSeriesSeq = aSplitSeries[nVectorPos]; 1543 sal_Int32 nLength = rAxisSeriesSeq.getLength(); 1544 rAxisSeriesSeq.realloc(nLength + 1); 1545 rAxisSeriesSeq[nLength] = xSeries; 1546 } 1547 // if the first series attached to secondary axis, then export those series first, which are attached to primary axis 1548 // also the MS Office export every time in this order 1549 if ( aSplitSeries.size() > 1 && nAxisIndexOfFirstSeries == 1 ) 1550 { 1551 std::swap( aSplitSeries[0], aSplitSeries[1] ); 1552 } 1553 } 1554 1555 return aSplitSeries; 1556 } 1557 1558 } 1559 1560 void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChartType ) 1561 { 1562 FSHelperPtr pFS = GetFS(); 1563 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType); 1564 for (auto & splitDataSeries : aSplitDataSeries) 1565 { 1566 if (!splitDataSeries.hasElements()) 1567 continue; 1568 1569 sal_Int32 nTypeId = XML_lineChart; 1570 if( mbIs3DChart ) 1571 nTypeId = XML_line3DChart; 1572 pFS->startElement(FSNS(XML_c, nTypeId)); 1573 1574 exportGrouping( ); 1575 1576 exportVaryColors(xChartType); 1577 // TODO: show marker symbol in series? 1578 bool bPrimaryAxes = true; 1579 exportSeries(xChartType, splitDataSeries, bPrimaryAxes); 1580 1581 // show marker? 1582 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE; 1583 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY); 1584 if( GetProperty( xPropSet, "SymbolType" ) ) 1585 mAny >>= nSymbolType; 1586 1587 if( !mbIs3DChart ) 1588 { 1589 exportHiLowLines(); 1590 exportUpDownBars(xChartType); 1591 const char* marker = nSymbolType == css::chart::ChartSymbolType::NONE? "0":"1"; 1592 pFS->singleElement(FSNS(XML_c, XML_marker), XML_val, marker); 1593 } 1594 1595 exportAxesId(bPrimaryAxes, true); 1596 1597 pFS->endElement( FSNS( XML_c, nTypeId ) ); 1598 } 1599 } 1600 1601 void ChartExport::exportPieChart( const Reference< chart2::XChartType >& xChartType ) 1602 { 1603 sal_Int32 eChartType = getChartType( ); 1604 if(eChartType == chart::TYPEID_DOUGHNUT) 1605 { 1606 exportDoughnutChart( xChartType ); 1607 return; 1608 } 1609 FSHelperPtr pFS = GetFS(); 1610 sal_Int32 nTypeId = XML_pieChart; 1611 if( mbIs3DChart ) 1612 nTypeId = XML_pie3DChart; 1613 pFS->startElement(FSNS(XML_c, nTypeId)); 1614 1615 exportVaryColors(xChartType); 1616 1617 bool bPrimaryAxes = true; 1618 exportAllSeries(xChartType, bPrimaryAxes); 1619 1620 if( !mbIs3DChart ) 1621 { 1622 // firstSliceAng 1623 exportFirstSliceAng( ); 1624 } 1625 1626 pFS->endElement( FSNS( XML_c, nTypeId ) ); 1627 } 1628 1629 void ChartExport::exportRadarChart( const Reference< chart2::XChartType >& xChartType) 1630 { 1631 FSHelperPtr pFS = GetFS(); 1632 pFS->startElement(FSNS(XML_c, XML_radarChart)); 1633 1634 // radarStyle 1635 sal_Int32 eChartType = getChartType( ); 1636 const char* radarStyle = nullptr; 1637 if( eChartType == chart::TYPEID_RADARAREA ) 1638 radarStyle = "filled"; 1639 else 1640 radarStyle = "marker"; 1641 pFS->singleElement(FSNS(XML_c, XML_radarStyle), XML_val, radarStyle); 1642 1643 exportVaryColors(xChartType); 1644 bool bPrimaryAxes = true; 1645 exportAllSeries(xChartType, bPrimaryAxes); 1646 exportAxesId(bPrimaryAxes); 1647 1648 pFS->endElement( FSNS( XML_c, XML_radarChart ) ); 1649 } 1650 1651 void ChartExport::exportScatterChartSeries( const Reference< chart2::XChartType >& xChartType, 1652 css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>>* pSeries) 1653 { 1654 FSHelperPtr pFS = GetFS(); 1655 pFS->startElement(FSNS(XML_c, XML_scatterChart)); 1656 // TODO:scatterStyle 1657 1658 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE; 1659 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY); 1660 if( GetProperty( xPropSet, "SymbolType" ) ) 1661 mAny >>= nSymbolType; 1662 1663 const char* scatterStyle = "lineMarker"; 1664 if (nSymbolType == css::chart::ChartSymbolType::NONE) 1665 { 1666 scatterStyle = "line"; 1667 } 1668 1669 pFS->singleElement(FSNS(XML_c, XML_scatterStyle), XML_val, scatterStyle); 1670 1671 exportVaryColors(xChartType); 1672 // FIXME: should export xVal and yVal 1673 bool bPrimaryAxes = true; 1674 if (pSeries) 1675 exportSeries(xChartType, *pSeries, bPrimaryAxes); 1676 exportAxesId(bPrimaryAxes); 1677 1678 pFS->endElement( FSNS( XML_c, XML_scatterChart ) ); 1679 } 1680 1681 void ChartExport::exportScatterChart( const Reference< chart2::XChartType >& xChartType ) 1682 { 1683 FSHelperPtr pFS = GetFS(); 1684 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType); 1685 bool bExported = false; 1686 for (auto & splitDataSeries : aSplitDataSeries) 1687 { 1688 if (!splitDataSeries.hasElements()) 1689 continue; 1690 1691 bExported = true; 1692 exportScatterChartSeries(xChartType, &splitDataSeries); 1693 } 1694 if (!bExported) 1695 exportScatterChartSeries(xChartType, nullptr); 1696 } 1697 1698 void ChartExport::exportStockChart( const Reference< chart2::XChartType >& xChartType ) 1699 { 1700 FSHelperPtr pFS = GetFS(); 1701 pFS->startElement(FSNS(XML_c, XML_stockChart)); 1702 1703 bool bPrimaryAxes = true; 1704 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY ); 1705 if(xDSCnt.is()) 1706 exportCandleStickSeries( xDSCnt->getDataSeries(), bPrimaryAxes ); 1707 1708 // export stock properties 1709 Reference< css::chart::XStatisticDisplay > xStockPropProvider( mxDiagram, uno::UNO_QUERY ); 1710 if( xStockPropProvider.is()) 1711 { 1712 exportHiLowLines(); 1713 exportUpDownBars(xChartType); 1714 } 1715 1716 exportAxesId(bPrimaryAxes); 1717 1718 pFS->endElement( FSNS( XML_c, XML_stockChart ) ); 1719 } 1720 1721 void ChartExport::exportHiLowLines() 1722 { 1723 FSHelperPtr pFS = GetFS(); 1724 // export the chart property 1725 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY ); 1726 1727 if (!xChartPropProvider.is()) 1728 return; 1729 1730 Reference< beans::XPropertySet > xStockPropSet = xChartPropProvider->getMinMaxLine(); 1731 if( !xStockPropSet.is() ) 1732 return; 1733 1734 pFS->startElement(FSNS(XML_c, XML_hiLowLines)); 1735 exportShapeProps( xStockPropSet ); 1736 pFS->endElement( FSNS( XML_c, XML_hiLowLines ) ); 1737 } 1738 1739 void ChartExport::exportUpDownBars( const Reference< chart2::XChartType >& xChartType) 1740 { 1741 if(xChartType->getChartType() != "com.sun.star.chart2.CandleStickChartType") 1742 return; 1743 1744 FSHelperPtr pFS = GetFS(); 1745 // export the chart property 1746 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY ); 1747 if(xChartPropProvider.is()) 1748 { 1749 // updownbar 1750 pFS->startElement(FSNS(XML_c, XML_upDownBars)); 1751 // TODO: gapWidth 1752 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(150)); 1753 1754 Reference< beans::XPropertySet > xChartPropSet = xChartPropProvider->getUpBar(); 1755 if( xChartPropSet.is() ) 1756 { 1757 pFS->startElement(FSNS(XML_c, XML_upBars)); 1758 // For Linechart with UpDownBars, spPr is not getting imported 1759 // so no need to call the exportShapeProps() for LineChart 1760 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType") 1761 { 1762 exportShapeProps(xChartPropSet); 1763 } 1764 pFS->endElement( FSNS( XML_c, XML_upBars ) ); 1765 } 1766 xChartPropSet = xChartPropProvider->getDownBar(); 1767 if( xChartPropSet.is() ) 1768 { 1769 pFS->startElement(FSNS(XML_c, XML_downBars)); 1770 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType") 1771 { 1772 exportShapeProps(xChartPropSet); 1773 } 1774 pFS->endElement( FSNS( XML_c, XML_downBars ) ); 1775 } 1776 pFS->endElement( FSNS( XML_c, XML_upDownBars ) ); 1777 } 1778 } 1779 1780 void ChartExport::exportSurfaceChart( const Reference< chart2::XChartType >& xChartType ) 1781 { 1782 FSHelperPtr pFS = GetFS(); 1783 sal_Int32 nTypeId = XML_surfaceChart; 1784 if( mbIs3DChart ) 1785 nTypeId = XML_surface3DChart; 1786 pFS->startElement(FSNS(XML_c, nTypeId)); 1787 exportVaryColors(xChartType); 1788 bool bPrimaryAxes = true; 1789 exportAllSeries(xChartType, bPrimaryAxes); 1790 exportAxesId(bPrimaryAxes); 1791 1792 pFS->endElement( FSNS( XML_c, nTypeId ) ); 1793 } 1794 1795 void ChartExport::exportAllSeries(const Reference<chart2::XChartType>& xChartType, bool& rPrimaryAxes) 1796 { 1797 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY ); 1798 if( ! xDSCnt.is()) 1799 return; 1800 1801 // export dataseries for current chart-type 1802 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries()); 1803 exportSeries(xChartType, aSeriesSeq, rPrimaryAxes); 1804 } 1805 1806 namespace { 1807 1808 Reference<chart2::XDataSeries> getPrimaryDataSeries(const Reference<chart2::XChartType>& xChartType) 1809 { 1810 Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY_THROW); 1811 1812 // export dataseries for current chart-type 1813 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries()); 1814 for (sal_Int32 nSeriesIdx=0; nSeriesIdx < aSeriesSeq.getLength(); ++nSeriesIdx) 1815 { 1816 Reference<chart2::XDataSeries> xSource(aSeriesSeq[nSeriesIdx], uno::UNO_QUERY); 1817 if (xSource.is()) 1818 return xSource; 1819 } 1820 1821 return Reference<chart2::XDataSeries>(); 1822 } 1823 1824 } 1825 1826 void ChartExport::exportVaryColors(const Reference<chart2::XChartType>& xChartType) 1827 { 1828 FSHelperPtr pFS = GetFS(); 1829 try 1830 { 1831 Reference<chart2::XDataSeries> xDataSeries = getPrimaryDataSeries(xChartType); 1832 Reference<beans::XPropertySet> xDataSeriesProps(xDataSeries, uno::UNO_QUERY_THROW); 1833 Any aAnyVaryColors = xDataSeriesProps->getPropertyValue("VaryColorsByPoint"); 1834 bool bVaryColors = false; 1835 aAnyVaryColors >>= bVaryColors; 1836 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, ToPsz10(bVaryColors)); 1837 } 1838 catch (...) 1839 { 1840 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0"); 1841 } 1842 } 1843 1844 void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType, 1845 Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq, bool& rPrimaryAxes ) 1846 { 1847 OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel(); 1848 OUString aChartType( xChartType->getChartType()); 1849 sal_Int32 eChartType = lcl_getChartType( aChartType ); 1850 1851 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<rSeriesSeq.getLength(); ++nSeriesIdx ) 1852 { 1853 // export series 1854 Reference< chart2::data::XDataSource > xSource( rSeriesSeq[nSeriesIdx], uno::UNO_QUERY ); 1855 if( xSource.is()) 1856 { 1857 Reference< chart2::XDataSeries > xDataSeries( xSource, uno::UNO_QUERY ); 1858 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt( 1859 xSource->getDataSequences()); 1860 // search for main sequence and create a series element 1861 { 1862 sal_Int32 nMainSequenceIndex = -1; 1863 sal_Int32 nSeriesLength = 0; 1864 Reference< chart2::data::XDataSequence > xValuesSeq; 1865 Reference< chart2::data::XDataSequence > xLabelSeq; 1866 sal_Int32 nSeqIdx=0; 1867 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx ) 1868 { 1869 OUString aRole; 1870 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() ); 1871 if( nMainSequenceIndex==-1 ) 1872 { 1873 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY ); 1874 if( xSeqProp.is()) 1875 xSeqProp->getPropertyValue("Role") >>= aRole; 1876 // "main" sequence 1877 if( aRole == aLabelRole ) 1878 { 1879 xValuesSeq.set( xTempValueSeq ); 1880 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel()); 1881 nMainSequenceIndex = nSeqIdx; 1882 } 1883 } 1884 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0)); 1885 if( nSeriesLength < nSequenceLength ) 1886 nSeriesLength = nSequenceLength; 1887 } 1888 1889 // have found the main sequence, then xValuesSeq and 1890 // xLabelSeq contain those. Otherwise both are empty 1891 { 1892 FSHelperPtr pFS = GetFS(); 1893 1894 pFS->startElement(FSNS(XML_c, XML_ser)); 1895 1896 // TODO: idx and order 1897 pFS->singleElement( FSNS( XML_c, XML_idx ), 1898 XML_val, OString::number(mnSeriesCount) ); 1899 pFS->singleElement( FSNS( XML_c, XML_order ), 1900 XML_val, OString::number(mnSeriesCount++) ); 1901 1902 // export label 1903 if( xLabelSeq.is() ) 1904 exportSeriesText( xLabelSeq ); 1905 1906 Reference<XPropertySet> xPropSet(xDataSeries, UNO_QUERY_THROW); 1907 if( GetProperty( xPropSet, "AttachedAxisIndex") ) 1908 { 1909 sal_Int32 nLocalAttachedAxis = 0; 1910 mAny >>= nLocalAttachedAxis; 1911 rPrimaryAxes = isPrimaryAxes(nLocalAttachedAxis); 1912 } 1913 1914 // export shape properties 1915 Reference< XPropertySet > xOldPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet( 1916 rSeriesSeq[nSeriesIdx], getModel() ); 1917 if( xOldPropSet.is() ) 1918 { 1919 exportShapeProps( xOldPropSet ); 1920 } 1921 1922 switch( eChartType ) 1923 { 1924 case chart::TYPEID_BUBBLE: 1925 case chart::TYPEID_HORBAR: 1926 case chart::TYPEID_BAR: 1927 { 1928 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0"); 1929 } 1930 break; 1931 case chart::TYPEID_LINE: 1932 { 1933 exportMarker(xDataSeries); 1934 break; 1935 } 1936 case chart::TYPEID_PIE: 1937 case chart::TYPEID_DOUGHNUT: 1938 { 1939 if( xOldPropSet.is() && GetProperty( xOldPropSet, "SegmentOffset") ) 1940 { 1941 sal_Int32 nOffset = 0; 1942 mAny >>= nOffset; 1943 pFS->singleElement( FSNS( XML_c, XML_explosion ), 1944 XML_val, OString::number( nOffset ) ); 1945 } 1946 break; 1947 } 1948 case chart::TYPEID_SCATTER: 1949 { 1950 exportMarker(xDataSeries); 1951 break; 1952 } 1953 case chart::TYPEID_RADARLINE: 1954 { 1955 exportMarker(xDataSeries); 1956 break; 1957 } 1958 } 1959 1960 // export data points 1961 exportDataPoints( uno::Reference< beans::XPropertySet >( rSeriesSeq[nSeriesIdx], uno::UNO_QUERY ), nSeriesLength, eChartType ); 1962 1963 // export data labels 1964 exportDataLabels(rSeriesSeq[nSeriesIdx], nSeriesLength, eChartType); 1965 1966 exportTrendlines( rSeriesSeq[nSeriesIdx] ); 1967 1968 if( eChartType != chart::TYPEID_PIE && 1969 eChartType != chart::TYPEID_RADARLINE ) 1970 { 1971 //export error bars here 1972 Reference< XPropertySet > xSeriesPropSet( xSource, uno::UNO_QUERY ); 1973 Reference< XPropertySet > xErrorBarYProps; 1974 xSeriesPropSet->getPropertyValue("ErrorBarY") >>= xErrorBarYProps; 1975 if(xErrorBarYProps.is()) 1976 exportErrorBar(xErrorBarYProps, true); 1977 if (eChartType != chart::TYPEID_BAR && 1978 eChartType != chart::TYPEID_HORBAR) 1979 { 1980 Reference< XPropertySet > xErrorBarXProps; 1981 xSeriesPropSet->getPropertyValue("ErrorBarX") >>= xErrorBarXProps; 1982 if(xErrorBarXProps.is()) 1983 exportErrorBar(xErrorBarXProps, false); 1984 } 1985 } 1986 1987 // export categories 1988 if( eChartType != chart::TYPEID_SCATTER && eChartType != chart::TYPEID_BUBBLE && mxCategoriesValues.is() ) 1989 exportSeriesCategory( mxCategoriesValues ); 1990 1991 if( (eChartType == chart::TYPEID_SCATTER) 1992 || (eChartType == chart::TYPEID_BUBBLE) ) 1993 { 1994 // export xVal 1995 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-x" ) ); 1996 if( xSequence.is() ) 1997 { 1998 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() ); 1999 if( xValues.is() ) 2000 exportSeriesValues( xValues, XML_xVal ); 2001 } 2002 } 2003 2004 if( eChartType == chart::TYPEID_BUBBLE ) 2005 { 2006 // export yVal 2007 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-y" ) ); 2008 if( xSequence.is() ) 2009 { 2010 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() ); 2011 if( xValues.is() ) 2012 exportSeriesValues( xValues, XML_yVal ); 2013 } 2014 } 2015 2016 // export values 2017 if( xValuesSeq.is() ) 2018 { 2019 sal_Int32 nYValueType = XML_val; 2020 if( eChartType == chart::TYPEID_SCATTER ) 2021 nYValueType = XML_yVal; 2022 else if( eChartType == chart::TYPEID_BUBBLE ) 2023 nYValueType = XML_bubbleSize; 2024 exportSeriesValues( xValuesSeq, nYValueType ); 2025 } 2026 2027 if( eChartType == chart::TYPEID_SCATTER 2028 || eChartType == chart::TYPEID_LINE ) 2029 exportSmooth(); 2030 2031 pFS->endElement( FSNS( XML_c, XML_ser ) ); 2032 } 2033 } 2034 } 2035 } 2036 } 2037 2038 void ChartExport::exportCandleStickSeries( 2039 const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq, 2040 bool& rPrimaryAxes) 2041 { 2042 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeriesSeq.getLength(); ++nSeriesIdx ) 2043 { 2044 Reference< chart2::XDataSeries > xSeries( aSeriesSeq[nSeriesIdx] ); 2045 rPrimaryAxes = lcl_isSeriesAttachedToFirstAxis(xSeries); 2046 2047 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY ); 2048 if( xSource.is()) 2049 { 2050 // export series in correct order (as we don't store roles) 2051 // with japanese candlesticks: open, low, high, close 2052 // otherwise: low, high, close 2053 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt( 2054 xSource->getDataSequences()); 2055 2056 const char* sSeries[] = {"values-first","values-max","values-min","values-last",nullptr}; 2057 2058 for( sal_Int32 idx = 0; sSeries[idx] != nullptr ; idx++ ) 2059 { 2060 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt, OUString::createFromAscii(sSeries[idx]) ) ); 2061 if( xLabeledSeq.is()) 2062 { 2063 Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel()); 2064 Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues()); 2065 { 2066 FSHelperPtr pFS = GetFS(); 2067 pFS->startElement(FSNS(XML_c, XML_ser)); 2068 2069 // TODO: idx and order 2070 // idx attribute should start from 1 and not from 0. 2071 pFS->singleElement( FSNS( XML_c, XML_idx ), 2072 XML_val, OString::number(idx+1) ); 2073 pFS->singleElement( FSNS( XML_c, XML_order ), 2074 XML_val, OString::number(idx+1) ); 2075 2076 // export label 2077 if( xLabelSeq.is() ) 2078 exportSeriesText( xLabelSeq ); 2079 2080 // TODO:export shape properties 2081 2082 // export categories 2083 if( mxCategoriesValues.is() ) 2084 exportSeriesCategory( mxCategoriesValues ); 2085 2086 // export values 2087 if( xValueSeq.is() ) 2088 exportSeriesValues( xValueSeq ); 2089 2090 pFS->endElement( FSNS( XML_c, XML_ser ) ); 2091 } 2092 } 2093 } 2094 } 2095 } 2096 } 2097 2098 void ChartExport::exportSeriesText( const Reference< chart2::data::XDataSequence > & xValueSeq ) 2099 { 2100 FSHelperPtr pFS = GetFS(); 2101 pFS->startElement(FSNS(XML_c, XML_tx)); 2102 2103 OUString aCellRange = xValueSeq->getSourceRangeRepresentation(); 2104 aCellRange = parseFormula( aCellRange ); 2105 pFS->startElement(FSNS(XML_c, XML_strRef)); 2106 2107 pFS->startElement(FSNS(XML_c, XML_f)); 2108 pFS->writeEscaped( aCellRange ); 2109 pFS->endElement( FSNS( XML_c, XML_f ) ); 2110 2111 OUString aLabelString = lcl_getLabelString( xValueSeq ); 2112 pFS->startElement(FSNS(XML_c, XML_strCache)); 2113 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, "1"); 2114 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, "0"); 2115 pFS->startElement(FSNS(XML_c, XML_v)); 2116 pFS->writeEscaped( aLabelString ); 2117 pFS->endElement( FSNS( XML_c, XML_v ) ); 2118 pFS->endElement( FSNS( XML_c, XML_pt ) ); 2119 pFS->endElement( FSNS( XML_c, XML_strCache ) ); 2120 pFS->endElement( FSNS( XML_c, XML_strRef ) ); 2121 pFS->endElement( FSNS( XML_c, XML_tx ) ); 2122 } 2123 2124 void ChartExport::exportSeriesCategory( const Reference< chart2::data::XDataSequence > & xValueSeq ) 2125 { 2126 FSHelperPtr pFS = GetFS(); 2127 pFS->startElement(FSNS(XML_c, XML_cat)); 2128 2129 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString(); 2130 aCellRange = parseFormula( aCellRange ); 2131 // TODO: need to handle XML_multiLvlStrRef according to aCellRange 2132 pFS->startElement(FSNS(XML_c, XML_strRef)); 2133 2134 pFS->startElement(FSNS(XML_c, XML_f)); 2135 pFS->writeEscaped( aCellRange ); 2136 pFS->endElement( FSNS( XML_c, XML_f ) ); 2137 2138 ::std::vector< OUString > aCategories; 2139 lcl_fillCategoriesIntoStringVector( xValueSeq, aCategories ); 2140 sal_Int32 ptCount = aCategories.size(); 2141 pFS->startElement(FSNS(XML_c, XML_strCache)); 2142 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount)); 2143 for( sal_Int32 i = 0; i < ptCount; i++ ) 2144 { 2145 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i)); 2146 pFS->startElement(FSNS(XML_c, XML_v)); 2147 pFS->writeEscaped( aCategories[i] ); 2148 pFS->endElement( FSNS( XML_c, XML_v ) ); 2149 pFS->endElement( FSNS( XML_c, XML_pt ) ); 2150 } 2151 2152 pFS->endElement( FSNS( XML_c, XML_strCache ) ); 2153 pFS->endElement( FSNS( XML_c, XML_strRef ) ); 2154 pFS->endElement( FSNS( XML_c, XML_cat ) ); 2155 } 2156 2157 void ChartExport::exportSeriesValues( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType ) 2158 { 2159 FSHelperPtr pFS = GetFS(); 2160 pFS->startElement(FSNS(XML_c, nValueType)); 2161 2162 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString(); 2163 aCellRange = parseFormula( aCellRange ); 2164 // TODO: need to handle XML_multiLvlStrRef according to aCellRange 2165 pFS->startElement(FSNS(XML_c, XML_numRef)); 2166 2167 pFS->startElement(FSNS(XML_c, XML_f)); 2168 pFS->writeEscaped( aCellRange ); 2169 pFS->endElement( FSNS( XML_c, XML_f ) ); 2170 2171 ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq ); 2172 sal_Int32 ptCount = aValues.size(); 2173 pFS->startElement(FSNS(XML_c, XML_numCache)); 2174 pFS->startElement(FSNS(XML_c, XML_formatCode)); 2175 // TODO: what format code? 2176 pFS->writeEscaped( "General" ); 2177 pFS->endElement( FSNS( XML_c, XML_formatCode ) ); 2178 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount)); 2179 2180 bool bIsNumberValue = true; 2181 bool bXSeriesValue = false; 2182 double Value = 1.0; 2183 2184 if(nValueType == XML_xVal) 2185 bXSeriesValue = true; 2186 2187 for( sal_Int32 i = 0; i < ptCount; i++ ) 2188 { 2189 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i)); 2190 pFS->startElement(FSNS(XML_c, XML_v)); 2191 if (bIsNumberValue && !rtl::math::isNan(aValues[i])) 2192 pFS->write( aValues[i] ); 2193 else if(bXSeriesValue) 2194 { 2195 //In Case aValues is not a number for X Values...We write X values as 1,2,3....MS Word does the same thing. 2196 pFS->write( Value ); 2197 Value = Value + 1; 2198 bIsNumberValue = false; 2199 } 2200 pFS->endElement( FSNS( XML_c, XML_v ) ); 2201 pFS->endElement( FSNS( XML_c, XML_pt ) ); 2202 } 2203 2204 pFS->endElement( FSNS( XML_c, XML_numCache ) ); 2205 pFS->endElement( FSNS( XML_c, XML_numRef ) ); 2206 pFS->endElement( FSNS( XML_c, nValueType ) ); 2207 } 2208 2209 void ChartExport::exportShapeProps( const Reference< XPropertySet >& xPropSet ) 2210 { 2211 FSHelperPtr pFS = GetFS(); 2212 pFS->startElement(FSNS(XML_c, XML_spPr)); 2213 2214 exportFill( xPropSet ); 2215 WriteOutline( xPropSet, getModel() ); 2216 2217 pFS->endElement( FSNS( XML_c, XML_spPr ) ); 2218 } 2219 2220 void ChartExport::exportTextProps(const Reference<XPropertySet>& xPropSet) 2221 { 2222 FSHelperPtr pFS = GetFS(); 2223 pFS->startElement(FSNS(XML_c, XML_txPr)); 2224 2225 sal_Int32 nRotation = 0; 2226 if (auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xPropSet, uno::UNO_QUERY)) 2227 { 2228 double fMultiplier = 0; 2229 // We have at least two possible units of returned value: degrees (e.g., for data labels), 2230 // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping 2231 // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while 2232 // the former is double. So we could test the contained type to decide which multiplier to 2233 // use. But testing the service info should be more robust. 2234 if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxis")) 2235 fMultiplier = -600.0; 2236 else if (xServiceInfo->supportsService("com.sun.star.chart2.DataSeries")) 2237 fMultiplier = -60000.0; 2238 2239 if (fMultiplier) 2240 { 2241 double fTextRotation = 0.0; 2242 uno::Any aAny = xPropSet->getPropertyValue("TextRotation"); 2243 if (aAny.hasValue() && (aAny >>= fTextRotation)) 2244 { 2245 // The MS Office UI allows values only in range of [-90,90]. 2246 if (fTextRotation > 9000.0 && fTextRotation < 27000.0) 2247 { 2248 // Reflect the angle if the value is between 90° and 270° 2249 fTextRotation -= 18000.0; 2250 } 2251 else if (fTextRotation >=27000.0) 2252 { 2253 fTextRotation -= 36000.0; 2254 } 2255 nRotation = std::round(fTextRotation * fMultiplier); 2256 } 2257 } 2258 } 2259 2260 if (nRotation) 2261 pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_rot, OString::number(nRotation)); 2262 else 2263 pFS->singleElement(FSNS(XML_a, XML_bodyPr)); 2264 2265 pFS->singleElement(FSNS(XML_a, XML_lstStyle)); 2266 2267 pFS->startElement(FSNS(XML_a, XML_p)); 2268 pFS->startElement(FSNS(XML_a, XML_pPr)); 2269 2270 WriteRunProperties(xPropSet, false, XML_defRPr, true, o3tl::temporary(false), 2271 o3tl::temporary(sal_Int32())); 2272 2273 pFS->endElement(FSNS(XML_a, XML_pPr)); 2274 pFS->endElement(FSNS(XML_a, XML_p)); 2275 pFS->endElement(FSNS(XML_c, XML_txPr)); 2276 } 2277 2278 void ChartExport::InitPlotArea( ) 2279 { 2280 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY); 2281 2282 // Check for supported services and then the properties provided by this service. 2283 Reference<lang::XServiceInfo> xServiceInfo (mxDiagram, uno::UNO_QUERY); 2284 if (xServiceInfo.is()) 2285 { 2286 if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxisZSupplier")) 2287 { 2288 xDiagramProperties->getPropertyValue("HasZAxis") >>= mbHasZAxis; 2289 } 2290 } 2291 2292 xDiagramProperties->getPropertyValue("Dim3D") >>= mbIs3DChart; 2293 2294 if( mbHasCategoryLabels && mxNewDiagram.is()) 2295 { 2296 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( mxNewDiagram ) ); 2297 if( xCategories.is() ) 2298 { 2299 mxCategoriesValues.set( xCategories->getValues() ); 2300 } 2301 } 2302 } 2303 2304 void ChartExport::exportAxes( ) 2305 { 2306 sal_Int32 nSize = maAxes.size(); 2307 // let's export the axis types in the right order 2308 for ( sal_Int32 nSortIdx = AXIS_PRIMARY_X; nSortIdx <= AXIS_SECONDARY_Y; nSortIdx++ ) 2309 { 2310 for ( sal_Int32 nIdx = 0; nIdx < nSize; nIdx++ ) 2311 { 2312 if (nSortIdx == maAxes[nIdx].nAxisType) 2313 exportAxis( maAxes[nIdx] ); 2314 } 2315 } 2316 } 2317 2318 namespace { 2319 2320 sal_Int32 getXAxisType(sal_Int32 eChartType) 2321 { 2322 if( (eChartType == chart::TYPEID_SCATTER) 2323 || (eChartType == chart::TYPEID_BUBBLE) ) 2324 return XML_valAx; 2325 else if( eChartType == chart::TYPEID_STOCK ) 2326 return XML_dateAx; 2327 2328 return XML_catAx; 2329 } 2330 2331 } 2332 2333 void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair) 2334 { 2335 // get some properties from document first 2336 bool bHasXAxisTitle = false, 2337 bHasYAxisTitle = false, 2338 bHasZAxisTitle = false, 2339 bHasSecondaryXAxisTitle = false, 2340 bHasSecondaryYAxisTitle = false; 2341 bool bHasXAxisMajorGrid = false, 2342 bHasXAxisMinorGrid = false, 2343 bHasYAxisMajorGrid = false, 2344 bHasYAxisMinorGrid = false, 2345 bHasZAxisMajorGrid = false, 2346 bHasZAxisMinorGrid = false; 2347 2348 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY); 2349 2350 xDiagramProperties->getPropertyValue("HasXAxisTitle") >>= bHasXAxisTitle; 2351 xDiagramProperties->getPropertyValue("HasYAxisTitle") >>= bHasYAxisTitle; 2352 xDiagramProperties->getPropertyValue("HasZAxisTitle") >>= bHasZAxisTitle; 2353 xDiagramProperties->getPropertyValue("HasSecondaryXAxisTitle") >>= bHasSecondaryXAxisTitle; 2354 xDiagramProperties->getPropertyValue("HasSecondaryYAxisTitle") >>= bHasSecondaryYAxisTitle; 2355 2356 xDiagramProperties->getPropertyValue("HasXAxisGrid") >>= bHasXAxisMajorGrid; 2357 xDiagramProperties->getPropertyValue("HasYAxisGrid") >>= bHasYAxisMajorGrid; 2358 xDiagramProperties->getPropertyValue("HasZAxisGrid") >>= bHasZAxisMajorGrid; 2359 2360 xDiagramProperties->getPropertyValue("HasXAxisHelpGrid") >>= bHasXAxisMinorGrid; 2361 xDiagramProperties->getPropertyValue("HasYAxisHelpGrid") >>= bHasYAxisMinorGrid; 2362 xDiagramProperties->getPropertyValue("HasZAxisHelpGrid") >>= bHasZAxisMinorGrid; 2363 2364 Reference< XPropertySet > xAxisProp; 2365 Reference< drawing::XShape > xAxisTitle; 2366 Reference< beans::XPropertySet > xMajorGrid; 2367 Reference< beans::XPropertySet > xMinorGrid; 2368 sal_Int32 nAxisType = XML_catAx; 2369 const char* sAxPos = nullptr; 2370 2371 switch( rAxisIdPair.nAxisType ) 2372 { 2373 case AXIS_PRIMARY_X: 2374 { 2375 Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY ); 2376 if( xAxisXSupp.is()) 2377 xAxisProp = xAxisXSupp->getXAxis(); 2378 if( bHasXAxisTitle ) 2379 xAxisTitle.set( xAxisXSupp->getXAxisTitle(), uno::UNO_QUERY ); 2380 if( bHasXAxisMajorGrid ) 2381 xMajorGrid.set( xAxisXSupp->getXMainGrid(), uno::UNO_QUERY ); 2382 if( bHasXAxisMinorGrid ) 2383 xMinorGrid.set( xAxisXSupp->getXHelpGrid(), uno::UNO_QUERY ); 2384 2385 sal_Int32 eChartType = getChartType(); 2386 nAxisType = getXAxisType(eChartType); 2387 // FIXME: axPos, need to check axis direction 2388 sAxPos = "b"; 2389 break; 2390 } 2391 case AXIS_PRIMARY_Y: 2392 { 2393 Reference< css::chart::XAxisYSupplier > xAxisYSupp( mxDiagram, uno::UNO_QUERY ); 2394 if( xAxisYSupp.is()) 2395 xAxisProp = xAxisYSupp->getYAxis(); 2396 if( bHasYAxisTitle ) 2397 xAxisTitle.set( xAxisYSupp->getYAxisTitle(), uno::UNO_QUERY ); 2398 if( bHasYAxisMajorGrid ) 2399 xMajorGrid.set( xAxisYSupp->getYMainGrid(), uno::UNO_QUERY ); 2400 if( bHasYAxisMinorGrid ) 2401 xMinorGrid.set( xAxisYSupp->getYHelpGrid(), uno::UNO_QUERY ); 2402 2403 nAxisType = XML_valAx; 2404 // FIXME: axPos, need to check axis direction 2405 sAxPos = "l"; 2406 break; 2407 } 2408 case AXIS_PRIMARY_Z: 2409 { 2410 Reference< css::chart::XAxisZSupplier > xAxisZSupp( mxDiagram, uno::UNO_QUERY ); 2411 if( xAxisZSupp.is()) 2412 xAxisProp = xAxisZSupp->getZAxis(); 2413 if( bHasZAxisTitle ) 2414 xAxisTitle.set( xAxisZSupp->getZAxisTitle(), uno::UNO_QUERY ); 2415 if( bHasZAxisMajorGrid ) 2416 xMajorGrid.set( xAxisZSupp->getZMainGrid(), uno::UNO_QUERY ); 2417 if( bHasZAxisMinorGrid ) 2418 xMinorGrid.set( xAxisZSupp->getZHelpGrid(), uno::UNO_QUERY ); 2419 2420 sal_Int32 eChartType = getChartType( ); 2421 if( (eChartType == chart::TYPEID_SCATTER) 2422 || (eChartType == chart::TYPEID_BUBBLE) ) 2423 nAxisType = XML_valAx; 2424 else if( eChartType == chart::TYPEID_STOCK ) 2425 nAxisType = XML_dateAx; 2426 else if( eChartType == chart::TYPEID_BAR ) 2427 nAxisType = XML_serAx; 2428 // FIXME: axPos, need to check axis direction 2429 sAxPos = "b"; 2430 break; 2431 } 2432 case AXIS_SECONDARY_X: 2433 { 2434 Reference< css::chart::XTwoAxisXSupplier > xAxisTwoXSupp( mxDiagram, uno::UNO_QUERY ); 2435 if( xAxisTwoXSupp.is()) 2436 xAxisProp = xAxisTwoXSupp->getSecondaryXAxis(); 2437 if( bHasSecondaryXAxisTitle ) 2438 { 2439 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY ); 2440 xAxisTitle.set( xAxisSupp->getSecondXAxisTitle(), uno::UNO_QUERY ); 2441 } 2442 2443 sal_Int32 eChartType = getChartType(); 2444 nAxisType = getXAxisType(eChartType); 2445 // FIXME: axPos, need to check axis direction 2446 sAxPos = "t"; 2447 break; 2448 } 2449 case AXIS_SECONDARY_Y: 2450 { 2451 Reference< css::chart::XTwoAxisYSupplier > xAxisTwoYSupp( mxDiagram, uno::UNO_QUERY ); 2452 if( xAxisTwoYSupp.is()) 2453 xAxisProp = xAxisTwoYSupp->getSecondaryYAxis(); 2454 if( bHasSecondaryYAxisTitle ) 2455 { 2456 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY ); 2457 xAxisTitle.set( xAxisSupp->getSecondYAxisTitle(), uno::UNO_QUERY ); 2458 } 2459 2460 nAxisType = XML_valAx; 2461 // FIXME: axPos, need to check axis direction 2462 sAxPos = "r"; 2463 break; 2464 } 2465 } 2466 2467 _exportAxis(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType, sAxPos, rAxisIdPair); 2468 } 2469 2470 void ChartExport::_exportAxis( 2471 const Reference< XPropertySet >& xAxisProp, 2472 const Reference< drawing::XShape >& xAxisTitle, 2473 const Reference< XPropertySet >& xMajorGrid, 2474 const Reference< XPropertySet >& xMinorGrid, 2475 sal_Int32 nAxisType, 2476 const char* sAxisPos, 2477 const AxisIdPair& rAxisIdPair ) 2478 { 2479 FSHelperPtr pFS = GetFS(); 2480 pFS->startElement(FSNS(XML_c, nAxisType)); 2481 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(rAxisIdPair.nAxisId)); 2482 2483 pFS->startElement(FSNS(XML_c, XML_scaling)); 2484 2485 // logBase, min, max 2486 if(GetProperty( xAxisProp, "Logarithmic" ) ) 2487 { 2488 bool bLogarithmic = false; 2489 mAny >>= bLogarithmic; 2490 if( bLogarithmic ) 2491 { 2492 // default value is 10? 2493 pFS->singleElement(FSNS(XML_c, XML_logBase), XML_val, OString::number(10)); 2494 } 2495 } 2496 2497 // orientation: minMax, maxMin 2498 bool bReverseDirection = false; 2499 if(GetProperty( xAxisProp, "ReverseDirection" ) ) 2500 mAny >>= bReverseDirection; 2501 2502 const char* orientation = bReverseDirection ? "maxMin":"minMax"; 2503 pFS->singleElement(FSNS(XML_c, XML_orientation), XML_val, orientation); 2504 2505 bool bAutoMax = false; 2506 if(GetProperty( xAxisProp, "AutoMax" ) ) 2507 mAny >>= bAutoMax; 2508 2509 if( !bAutoMax && (GetProperty( xAxisProp, "Max" ) ) ) 2510 { 2511 double dMax = 0; 2512 mAny >>= dMax; 2513 pFS->singleElement(FSNS(XML_c, XML_max), XML_val, OString::number(dMax)); 2514 } 2515 2516 bool bAutoMin = false; 2517 if(GetProperty( xAxisProp, "AutoMin" ) ) 2518 mAny >>= bAutoMin; 2519 2520 if( !bAutoMin && (GetProperty( xAxisProp, "Min" ) ) ) 2521 { 2522 double dMin = 0; 2523 mAny >>= dMin; 2524 pFS->singleElement(FSNS(XML_c, XML_min), XML_val, OString::number(dMin)); 2525 } 2526 2527 pFS->endElement( FSNS( XML_c, XML_scaling ) ); 2528 2529 bool bVisible = true; 2530 if( xAxisProp.is() ) 2531 { 2532 xAxisProp->getPropertyValue("Visible") >>= bVisible; 2533 } 2534 2535 // only export each axis only once non-deleted 2536 bool bDeleted = maExportedAxis.find(rAxisIdPair.nAxisType) != maExportedAxis.end(); 2537 2538 if (!bDeleted) 2539 maExportedAxis.insert(rAxisIdPair.nAxisType); 2540 2541 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1"); 2542 2543 // FIXME: axPos, need to check the property "ReverseDirection" 2544 pFS->singleElement(FSNS(XML_c, XML_axPos), XML_val, sAxisPos); 2545 // major grid line 2546 if( xMajorGrid.is()) 2547 { 2548 pFS->startElement(FSNS(XML_c, XML_majorGridlines)); 2549 exportShapeProps( xMajorGrid ); 2550 pFS->endElement( FSNS( XML_c, XML_majorGridlines ) ); 2551 } 2552 2553 // minor grid line 2554 if( xMinorGrid.is()) 2555 { 2556 pFS->startElement(FSNS(XML_c, XML_minorGridlines)); 2557 exportShapeProps( xMinorGrid ); 2558 pFS->endElement( FSNS( XML_c, XML_minorGridlines ) ); 2559 } 2560 2561 // title 2562 if( xAxisTitle.is() ) 2563 exportTitle( xAxisTitle ); 2564 2565 bool bLinkedNumFmt = true; 2566 if (GetProperty(xAxisProp, "LinkNumberFormatToSource")) 2567 mAny >>= bLinkedNumFmt; 2568 2569 OUString aNumberFormatString("General"); 2570 if (GetProperty(xAxisProp, "NumberFormat")) 2571 { 2572 sal_Int32 nKey = 0; 2573 mAny >>= nKey; 2574 aNumberFormatString = getNumberFormatCode(nKey); 2575 } 2576 2577 OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8); 2578 pFS->singleElement(FSNS(XML_c, XML_numFmt), 2579 XML_formatCode, sNumberFormatString.getStr(), 2580 XML_sourceLinked, bLinkedNumFmt ? "1" : "0"); 2581 2582 // majorTickMark 2583 sal_Int32 nValue = 0; 2584 if(GetProperty( xAxisProp, "Marks" ) ) 2585 { 2586 mAny >>= nValue; 2587 bool bInner = nValue & css::chart::ChartAxisMarks::INNER; 2588 bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER; 2589 const char* majorTickMark = nullptr; 2590 if( bInner && bOuter ) 2591 majorTickMark = "cross"; 2592 else if( bInner ) 2593 majorTickMark = "in"; 2594 else if( bOuter ) 2595 majorTickMark = "out"; 2596 else 2597 majorTickMark = "none"; 2598 pFS->singleElement(FSNS(XML_c, XML_majorTickMark), XML_val, majorTickMark); 2599 } 2600 // minorTickMark 2601 if(GetProperty( xAxisProp, "HelpMarks" ) ) 2602 { 2603 mAny >>= nValue; 2604 bool bInner = nValue & css::chart::ChartAxisMarks::INNER; 2605 bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER; 2606 const char* minorTickMark = nullptr; 2607 if( bInner && bOuter ) 2608 minorTickMark = "cross"; 2609 else if( bInner ) 2610 minorTickMark = "in"; 2611 else if( bOuter ) 2612 minorTickMark = "out"; 2613 else 2614 minorTickMark = "none"; 2615 pFS->singleElement(FSNS(XML_c, XML_minorTickMark), XML_val, minorTickMark); 2616 } 2617 // tickLblPos 2618 const char* sTickLblPos = nullptr; 2619 bool bDisplayLabel = true; 2620 if(GetProperty( xAxisProp, "DisplayLabels" ) ) 2621 mAny >>= bDisplayLabel; 2622 if( bDisplayLabel && (GetProperty( xAxisProp, "LabelPosition" ) ) ) 2623 { 2624 css::chart::ChartAxisLabelPosition eLabelPosition = css::chart::ChartAxisLabelPosition_NEAR_AXIS; 2625 mAny >>= eLabelPosition; 2626 switch( eLabelPosition ) 2627 { 2628 case css::chart::ChartAxisLabelPosition_NEAR_AXIS: 2629 case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: 2630 sTickLblPos = "nextTo"; 2631 break; 2632 case css::chart::ChartAxisLabelPosition_OUTSIDE_START: 2633 sTickLblPos = "low"; 2634 break; 2635 case css::chart::ChartAxisLabelPosition_OUTSIDE_END: 2636 sTickLblPos = "high"; 2637 break; 2638 default: 2639 sTickLblPos = "nextTo"; 2640 break; 2641 } 2642 } 2643 else 2644 { 2645 sTickLblPos = "none"; 2646 } 2647 pFS->singleElement(FSNS(XML_c, XML_tickLblPos), XML_val, sTickLblPos); 2648 2649 // shape properties 2650 exportShapeProps( xAxisProp ); 2651 2652 exportTextProps(xAxisProp); 2653 2654 pFS->singleElement(FSNS(XML_c, XML_crossAx), XML_val, OString::number(rAxisIdPair.nCrossAx)); 2655 2656 // crosses & crossesAt 2657 bool bCrossesValue = false; 2658 const char* sCrosses = nullptr; 2659 // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible 2660 if( GetProperty( xAxisProp, "CrossoverPosition" ) && !bDeleted && bVisible ) 2661 { 2662 css::chart::ChartAxisPosition ePosition( css::chart::ChartAxisPosition_ZERO ); 2663 mAny >>= ePosition; 2664 switch( ePosition ) 2665 { 2666 case css::chart::ChartAxisPosition_START: 2667 sCrosses = "min"; 2668 break; 2669 case css::chart::ChartAxisPosition_END: 2670 sCrosses = "max"; 2671 break; 2672 case css::chart::ChartAxisPosition_ZERO: 2673 sCrosses = "autoZero"; 2674 break; 2675 default: 2676 bCrossesValue = true; 2677 break; 2678 } 2679 } 2680 2681 if( bCrossesValue && GetProperty( xAxisProp, "CrossoverValue" ) ) 2682 { 2683 double dValue = 0; 2684 mAny >>= dValue; 2685 pFS->singleElement(FSNS(XML_c, XML_crossesAt), XML_val, OString::number(dValue)); 2686 } 2687 else 2688 { 2689 if(sCrosses) 2690 { 2691 pFS->singleElement(FSNS(XML_c, XML_crosses), XML_val, sCrosses); 2692 } 2693 } 2694 2695 if( ( nAxisType == XML_catAx ) 2696 || ( nAxisType == XML_dateAx ) ) 2697 { 2698 // FIXME: seems not support? use default value, 2699 const char* const isAuto = "1"; 2700 pFS->singleElement(FSNS(XML_c, XML_auto), XML_val, isAuto); 2701 2702 if( nAxisType == XML_catAx ) 2703 { 2704 // FIXME: seems not support? lblAlgn 2705 const char* const sLblAlgn = "ctr"; 2706 pFS->singleElement(FSNS(XML_c, XML_lblAlgn), XML_val, sLblAlgn); 2707 } 2708 2709 // FIXME: seems not support? lblOffset 2710 pFS->singleElement(FSNS(XML_c, XML_lblOffset), XML_val, OString::number(100)); 2711 } 2712 2713 // TODO: MSO does not support random axis cross position for 2714 // category axis, so we ideally need an algorithm that decides 2715 // when to map the crossing to the tick mark and when to the 2716 // middle of the category 2717 sal_Int32 nChartType = getChartType(); 2718 if (nAxisType == XML_valAx && (nChartType == chart::TYPEID_LINE || nChartType == chart::TYPEID_SCATTER)) 2719 { 2720 pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "midCat"); 2721 } 2722 2723 // majorUnit 2724 bool bAutoStepMain = false; 2725 if(GetProperty( xAxisProp, "AutoStepMain" ) ) 2726 mAny >>= bAutoStepMain; 2727 2728 if( !bAutoStepMain && (GetProperty( xAxisProp, "StepMain" ) ) ) 2729 { 2730 double dMajorUnit = 0; 2731 mAny >>= dMajorUnit; 2732 pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(dMajorUnit)); 2733 } 2734 // minorUnit 2735 bool bAutoStepHelp = false; 2736 if(GetProperty( xAxisProp, "AutoStepHelp" ) ) 2737 mAny >>= bAutoStepHelp; 2738 2739 if( !bAutoStepHelp && (GetProperty( xAxisProp, "StepHelp" ) ) ) 2740 { 2741 double dMinorUnit = 0; 2742 mAny >>= dMinorUnit; 2743 if( GetProperty( xAxisProp, "StepHelpCount" ) ) 2744 { 2745 sal_Int32 dMinorUnitCount = 0; 2746 mAny >>= dMinorUnitCount; 2747 // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel), 2748 // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible 2749 // to calculate StepHelpCount. 2750 if( dMinorUnitCount != 5 ) 2751 { 2752 pFS->singleElement( FSNS( XML_c, XML_minorUnit ), 2753 XML_val, OString::number( dMinorUnit ) ); 2754 } 2755 } 2756 } 2757 2758 if( nAxisType == XML_valAx && GetProperty( xAxisProp, "DisplayUnits" ) ) 2759 { 2760 bool bDisplayUnits = false; 2761 mAny >>= bDisplayUnits; 2762 if(bDisplayUnits) 2763 { 2764 OUString aVal; 2765 if(GetProperty( xAxisProp, "BuiltInUnit" )) 2766 { 2767 mAny >>= aVal; 2768 if(!aVal.isEmpty()) 2769 { 2770 pFS->startElement(FSNS(XML_c, XML_dispUnits)); 2771 2772 pFS->singleElement(FSNS(XML_c, XML_builtInUnit), XML_val, aVal.toUtf8()); 2773 2774 pFS->singleElement(FSNS( XML_c, XML_dispUnitsLbl )); 2775 pFS->endElement( FSNS( XML_c, XML_dispUnits ) ); 2776 } 2777 } 2778 } 2779 } 2780 2781 pFS->endElement( FSNS( XML_c, nAxisType ) ); 2782 } 2783 2784 namespace { 2785 2786 struct LabelPlacementParam 2787 { 2788 bool mbExport; 2789 sal_Int32 meDefault; 2790 2791 std::unordered_set<sal_Int32> maAllowedValues; 2792 2793 LabelPlacementParam() : 2794 mbExport(true), 2795 meDefault(css::chart::DataLabelPlacement::OUTSIDE) {} 2796 2797 void allowAll() 2798 { 2799 maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE); 2800 maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE); 2801 maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER); 2802 maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN); 2803 maAllowedValues.insert(css::chart::DataLabelPlacement::TOP); 2804 maAllowedValues.insert(css::chart::DataLabelPlacement::BOTTOM); 2805 maAllowedValues.insert(css::chart::DataLabelPlacement::LEFT); 2806 maAllowedValues.insert(css::chart::DataLabelPlacement::RIGHT); 2807 maAllowedValues.insert(css::chart::DataLabelPlacement::AVOID_OVERLAP); 2808 } 2809 }; 2810 2811 const char* toOOXMLPlacement( sal_Int32 nPlacement ) 2812 { 2813 switch (nPlacement) 2814 { 2815 case css::chart::DataLabelPlacement::OUTSIDE: return "outEnd"; 2816 case css::chart::DataLabelPlacement::INSIDE: return "inEnd"; 2817 case css::chart::DataLabelPlacement::CENTER: return "ctr"; 2818 case css::chart::DataLabelPlacement::NEAR_ORIGIN: return "inBase"; 2819 case css::chart::DataLabelPlacement::TOP: return "t"; 2820 case css::chart::DataLabelPlacement::BOTTOM: return "b"; 2821 case css::chart::DataLabelPlacement::LEFT: return "l"; 2822 case css::chart::DataLabelPlacement::RIGHT: return "r"; 2823 case css::chart::DataLabelPlacement::AVOID_OVERLAP: return "bestFit"; 2824 default: 2825 ; 2826 } 2827 2828 return "outEnd"; 2829 } 2830 2831 OUString getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType ) 2832 { 2833 switch (aType) 2834 { 2835 case chart2::DataPointCustomLabelFieldType_CATEGORYNAME: 2836 return OUString("CATEGORYNAME"); 2837 2838 case chart2::DataPointCustomLabelFieldType_SERIESNAME: 2839 return OUString("SERIESNAME"); 2840 2841 case chart2::DataPointCustomLabelFieldType_VALUE: 2842 return OUString("VALUE"); 2843 2844 case chart2::DataPointCustomLabelFieldType_CELLREF: 2845 return OUString("CELLREF"); 2846 2847 default: 2848 break; 2849 } 2850 return OUString(); 2851 } 2852 2853 void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> const & xPropertySet ) 2854 { 2855 bool bDummy = false; 2856 sal_Int32 nDummy; 2857 pChartExport->WriteRunProperties(xPropertySet, false, XML_rPr, true, bDummy, nDummy); 2858 } 2859 2860 void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport, 2861 const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields ) 2862 { 2863 pFS->startElement(FSNS(XML_c, XML_tx)); 2864 pFS->startElement(FSNS(XML_c, XML_rich)); 2865 2866 // TODO: body properties? 2867 pFS->singleElement(FSNS(XML_a, XML_bodyPr)); 2868 2869 OUString sFieldType; 2870 pFS->startElement(FSNS(XML_a, XML_p)); 2871 2872 for (auto& rField : rCustomLabelFields) 2873 { 2874 Reference<XPropertySet> xPropertySet(rField, UNO_QUERY); 2875 chart2::DataPointCustomLabelFieldType aType = rField->getFieldType(); 2876 sFieldType.clear(); 2877 bool bNewParagraph = false; 2878 2879 if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE) 2880 bNewParagraph = true; 2881 else if (aType != chart2::DataPointCustomLabelFieldType_TEXT) 2882 sFieldType = getFieldTypeString(aType); 2883 2884 if (bNewParagraph) 2885 { 2886 pFS->endElement(FSNS(XML_a, XML_p)); 2887 pFS->startElement(FSNS(XML_a, XML_p)); 2888 continue; 2889 } 2890 2891 if (sFieldType.isEmpty()) 2892 { 2893 // Normal text run 2894 pFS->startElement(FSNS(XML_a, XML_r)); 2895 writeRunProperties(pChartExport, xPropertySet); 2896 2897 pFS->startElement(FSNS(XML_a, XML_t)); 2898 pFS->writeEscaped(rField->getString()); 2899 pFS->endElement(FSNS(XML_a, XML_t)); 2900 2901 pFS->endElement(FSNS(XML_a, XML_r)); 2902 } 2903 else 2904 { 2905 // Field 2906 pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid().toUtf8(), XML_type, 2907 sFieldType.toUtf8()); 2908 writeRunProperties(pChartExport, xPropertySet); 2909 2910 pFS->startElement(FSNS(XML_a, XML_t)); 2911 pFS->writeEscaped(rField->getString()); 2912 pFS->endElement(FSNS(XML_a, XML_t)); 2913 2914 pFS->endElement(FSNS(XML_a, XML_fld)); 2915 } 2916 } 2917 2918 pFS->endElement(FSNS(XML_a, XML_p)); 2919 pFS->endElement(FSNS(XML_c, XML_rich)); 2920 pFS->endElement(FSNS(XML_c, XML_tx)); 2921 } 2922 2923 void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport, 2924 const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam ) 2925 { 2926 if (!xPropSet.is()) 2927 return; 2928 2929 chart2::DataPointLabel aLabel; 2930 Sequence<Reference<chart2::XDataPointCustomLabelField>> aCustomLabelFields; 2931 sal_Int32 nLabelBorderWidth = 0; 2932 sal_Int32 nLabelBorderColor = 0x00FFFFFF; 2933 2934 xPropSet->getPropertyValue("Label") >>= aLabel; 2935 xPropSet->getPropertyValue("CustomLabelFields") >>= aCustomLabelFields; 2936 xPropSet->getPropertyValue("LabelBorderWidth") >>= nLabelBorderWidth; 2937 xPropSet->getPropertyValue("LabelBorderColor") >>= nLabelBorderColor; 2938 2939 if (nLabelBorderWidth > 0) 2940 { 2941 pFS->startElement(FSNS(XML_c, XML_spPr)); 2942 pFS->startElement(FSNS(XML_a, XML_ln), XML_w, 2943 OString::number(convertHmmToEmu(nLabelBorderWidth))); 2944 if (nLabelBorderColor != -1) 2945 { 2946 pFS->startElement(FSNS(XML_a, XML_solidFill)); 2947 2948 OString aStr = OString::number(nLabelBorderColor, 16).toAsciiUpperCase(); 2949 pFS->singleElement(FSNS(XML_a, XML_srgbClr), XML_val, aStr); 2950 2951 pFS->endElement(FSNS(XML_a, XML_solidFill)); 2952 } 2953 pFS->endElement(FSNS(XML_a, XML_ln)); 2954 pFS->endElement(FSNS(XML_c, XML_spPr)); 2955 } 2956 2957 if (aCustomLabelFields.hasElements()) 2958 writeCustomLabel(pFS, pChartExport, aCustomLabelFields); 2959 2960 if (rLabelParam.mbExport) 2961 { 2962 sal_Int32 nLabelPlacement = rLabelParam.meDefault; 2963 if (xPropSet->getPropertyValue("LabelPlacement") >>= nLabelPlacement) 2964 { 2965 if (!rLabelParam.maAllowedValues.count(nLabelPlacement)) 2966 nLabelPlacement = rLabelParam.meDefault; 2967 pFS->singleElement(FSNS(XML_c, XML_dLblPos), XML_val, toOOXMLPlacement(nLabelPlacement)); 2968 } 2969 } 2970 2971 pFS->singleElement(FSNS(XML_c, XML_showLegendKey), XML_val, ToPsz10(aLabel.ShowLegendSymbol)); 2972 pFS->singleElement(FSNS(XML_c, XML_showVal), XML_val, ToPsz10(aLabel.ShowNumber)); 2973 pFS->singleElement(FSNS(XML_c, XML_showCatName), XML_val, ToPsz10(aLabel.ShowCategoryName)); 2974 pFS->singleElement(FSNS(XML_c, XML_showSerName), XML_val, ToPsz10(false)); 2975 pFS->singleElement(FSNS(XML_c, XML_showPercent), XML_val, ToPsz10(aLabel.ShowNumberInPercent)); 2976 2977 // Export the text "separator" if exists 2978 uno::Any aAny = xPropSet->getPropertyValue("LabelSeparator"); 2979 if( aAny.hasValue() ) 2980 { 2981 OUString nLabelSeparator; 2982 aAny >>= nLabelSeparator; 2983 pFS->startElement(FSNS(XML_c, XML_separator)); 2984 pFS->writeEscaped( nLabelSeparator ); 2985 pFS->endElement( FSNS( XML_c, XML_separator ) ); 2986 } 2987 } 2988 2989 } 2990 2991 void ChartExport::exportDataLabels( 2992 const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType ) 2993 { 2994 if (!xSeries.is() || nSeriesLength <= 0) 2995 return; 2996 2997 uno::Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY); 2998 if (!xPropSet.is()) 2999 return; 3000 3001 FSHelperPtr pFS = GetFS(); 3002 pFS->startElement(FSNS(XML_c, XML_dLbls)); 3003 3004 bool bLinkedNumFmt = true; 3005 if (GetProperty(xPropSet, "LinkNumberFormatToSource")) 3006 mAny >>= bLinkedNumFmt; 3007 3008 if (GetProperty(xPropSet, "NumberFormat") || GetProperty(xPropSet, "PercentageNumberFormat")) 3009 { 3010 sal_Int32 nKey = 0; 3011 mAny >>= nKey; 3012 3013 OUString aNumberFormatString = getNumberFormatCode(nKey); 3014 OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8); 3015 3016 pFS->singleElement(FSNS(XML_c, XML_numFmt), 3017 XML_formatCode, sNumberFormatString, 3018 XML_sourceLinked, ToPsz10(bLinkedNumFmt)); 3019 } 3020 3021 uno::Sequence<sal_Int32> aAttrLabelIndices; 3022 xPropSet->getPropertyValue("AttributedDataPoints") >>= aAttrLabelIndices; 3023 3024 // We must not export label placement property when the chart type doesn't 3025 // support this option in MS Office, else MS Office would think the file 3026 // is corrupt & refuse to open it. 3027 3028 const chart::TypeGroupInfo& rInfo = chart::GetTypeGroupInfo(static_cast<chart::TypeId>(eChartType)); 3029 LabelPlacementParam aParam; 3030 aParam.mbExport = !mbIs3DChart; 3031 aParam.meDefault = rInfo.mnDefLabelPos; 3032 aParam.allowAll(); 3033 switch (eChartType) // diagram chart type 3034 { 3035 case chart::TYPEID_PIE: 3036 if(getChartType() == chart::TYPEID_DOUGHNUT) 3037 aParam.mbExport = false; 3038 else 3039 // All pie charts support label placement. 3040 aParam.mbExport = true; 3041 break; 3042 case chart::TYPEID_AREA: 3043 case chart::TYPEID_RADARLINE: 3044 case chart::TYPEID_RADARAREA: 3045 // These chart types don't support label placement. 3046 aParam.mbExport = false; 3047 break; 3048 case chart::TYPEID_BAR: 3049 if (mbStacked || mbPercent) 3050 { 3051 aParam.maAllowedValues.clear(); 3052 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER); 3053 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE); 3054 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN); 3055 aParam.meDefault = css::chart::DataLabelPlacement::CENTER; 3056 } 3057 else // Clustered bar chart 3058 { 3059 aParam.maAllowedValues.clear(); 3060 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER); 3061 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE); 3062 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE); 3063 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN); 3064 aParam.meDefault = css::chart::DataLabelPlacement::OUTSIDE; 3065 } 3066 break; 3067 default: 3068 ; 3069 } 3070 3071 const sal_Int32* p = aAttrLabelIndices.getConstArray(); 3072 const sal_Int32* pEnd = p + aAttrLabelIndices.getLength(); 3073 3074 3075 for (; p != pEnd; ++p) 3076 { 3077 sal_Int32 nIdx = *p; 3078 uno::Reference<beans::XPropertySet> xLabelPropSet = xSeries->getDataPointByIndex(nIdx); 3079 3080 if (!xLabelPropSet.is()) 3081 continue; 3082 3083 pFS->startElement(FSNS(XML_c, XML_dLbl)); 3084 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nIdx)); 3085 3086 if (GetProperty(xLabelPropSet, "NumberFormat") || GetProperty(xLabelPropSet, "PercentageNumberFormat")) 3087 { 3088 sal_Int32 nKey = 0; 3089 mAny >>= nKey; 3090 3091 OUString aNumberFormatString = getNumberFormatCode(nKey); 3092 OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8); 3093 3094 pFS->singleElement(FSNS(XML_c, XML_numFmt), XML_formatCode, sNumberFormatString.getStr(), 3095 XML_sourceLinked, ToPsz10(bLinkedNumFmt)); 3096 } 3097 3098 // Individual label property that overwrites the baseline. 3099 exportTextProps( xLabelPropSet ); 3100 writeLabelProperties(pFS, this, xLabelPropSet, aParam); 3101 pFS->endElement(FSNS(XML_c, XML_dLbl)); 3102 } 3103 3104 exportTextProps( xPropSet ); 3105 // Baseline label properties for all labels. 3106 writeLabelProperties(pFS, this, xPropSet, aParam); 3107 3108 pFS->singleElement(FSNS(XML_c, XML_showLeaderLines), XML_val, "0"); 3109 3110 pFS->endElement(FSNS(XML_c, XML_dLbls)); 3111 } 3112 3113 void ChartExport::exportDataPoints( 3114 const uno::Reference< beans::XPropertySet > & xSeriesProperties, 3115 sal_Int32 nSeriesLength, sal_Int32 eChartType ) 3116 { 3117 uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY ); 3118 bool bVaryColorsByPoint = false; 3119 Sequence< sal_Int32 > aDataPointSeq; 3120 if( xSeriesProperties.is()) 3121 { 3122 Any aAny = xSeriesProperties->getPropertyValue( "AttributedDataPoints" ); 3123 aAny >>= aDataPointSeq; 3124 xSeriesProperties->getPropertyValue( "VaryColorsByPoint" ) >>= bVaryColorsByPoint; 3125 } 3126 3127 const sal_Int32 * pPoints = aDataPointSeq.getConstArray(); 3128 sal_Int32 nElement; 3129 Reference< chart2::XColorScheme > xColorScheme; 3130 if( mxNewDiagram.is()) 3131 xColorScheme.set( mxNewDiagram->getDefaultColorScheme()); 3132 3133 if( bVaryColorsByPoint && xColorScheme.is() ) 3134 { 3135 ::std::set< sal_Int32 > aAttrPointSet; 3136 ::std::copy( pPoints, pPoints + aDataPointSeq.getLength(), 3137 ::std::inserter( aAttrPointSet, aAttrPointSet.begin())); 3138 const ::std::set< sal_Int32 >::const_iterator aEndIt( aAttrPointSet.end()); 3139 for( nElement = 0; nElement < nSeriesLength; ++nElement ) 3140 { 3141 uno::Reference< beans::XPropertySet > xPropSet; 3142 if( aAttrPointSet.find( nElement ) != aEndIt ) 3143 { 3144 try 3145 { 3146 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet( 3147 xSeries, nElement, getModel() ); 3148 } 3149 catch( const uno::Exception & ) 3150 { 3151 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" ); 3152 } 3153 } 3154 else 3155 { 3156 // property set only containing the color 3157 xPropSet.set( new ColorPropertySet( xColorScheme->getColorByIndex( nElement ))); 3158 } 3159 3160 if( xPropSet.is() ) 3161 { 3162 FSHelperPtr pFS = GetFS(); 3163 pFS->startElement(FSNS(XML_c, XML_dPt)); 3164 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement)); 3165 3166 switch (eChartType) 3167 { 3168 case chart::TYPEID_PIE: 3169 case chart::TYPEID_DOUGHNUT: 3170 { 3171 if( xPropSet.is() && GetProperty( xPropSet, "SegmentOffset") ) 3172 { 3173 sal_Int32 nOffset = 0; 3174 mAny >>= nOffset; 3175 if (nOffset) 3176 pFS->singleElement( FSNS( XML_c, XML_explosion ), 3177 XML_val, OString::number( nOffset ) ); 3178 } 3179 break; 3180 } 3181 default: 3182 break; 3183 } 3184 exportShapeProps( xPropSet ); 3185 3186 pFS->endElement( FSNS( XML_c, XML_dPt ) ); 3187 } 3188 } 3189 } 3190 3191 // Export Data Point Property in Charts even if the VaryColors is false 3192 if( !bVaryColorsByPoint ) 3193 { 3194 ::std::set< sal_Int32 > aAttrPointSet; 3195 ::std::copy( pPoints, pPoints + aDataPointSeq.getLength(), 3196 ::std::inserter( aAttrPointSet, aAttrPointSet.begin())); 3197 const ::std::set< sal_Int32 >::const_iterator aEndIt( aAttrPointSet.end()); 3198 for( nElement = 0; nElement < nSeriesLength; ++nElement ) 3199 { 3200 uno::Reference< beans::XPropertySet > xPropSet; 3201 if( aAttrPointSet.find( nElement ) != aEndIt ) 3202 { 3203 try 3204 { 3205 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet( 3206 xSeries, nElement, getModel() ); 3207 } 3208 catch( const uno::Exception & ) 3209 { 3210 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" ); 3211 } 3212 } 3213 3214 if( xPropSet.is() ) 3215 { 3216 FSHelperPtr pFS = GetFS(); 3217 pFS->startElement(FSNS(XML_c, XML_dPt)); 3218 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement)); 3219 3220 switch( eChartType ) 3221 { 3222 case chart::TYPEID_BUBBLE: 3223 case chart::TYPEID_HORBAR: 3224 case chart::TYPEID_BAR: 3225 { 3226 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0"); 3227 } 3228 break; 3229 } 3230 exportShapeProps( xPropSet ); 3231 3232 pFS->endElement( FSNS( XML_c, XML_dPt ) ); 3233 } 3234 } 3235 } 3236 } 3237 3238 void ChartExport::exportAxesId(bool bPrimaryAxes, bool bCheckCombinedAxes) 3239 { 3240 sal_Int32 nAxisIdx, nAxisIdy; 3241 bool bPrimaryAxisExists = false; 3242 bool bSecondaryAxisExists = false; 3243 // let's check which axis already exists and which axis is attached to the actual dataseries 3244 if (maAxes.size() >= 2) 3245 { 3246 bPrimaryAxisExists = bPrimaryAxes && maAxes[1].nAxisType == AXIS_PRIMARY_Y; 3247 bSecondaryAxisExists = !bPrimaryAxes && maAxes[1].nAxisType == AXIS_SECONDARY_Y; 3248 } 3249 // tdf#114181 keep axes of combined charts 3250 if ( bCheckCombinedAxes && ( bPrimaryAxisExists || bSecondaryAxisExists ) ) 3251 { 3252 nAxisIdx = maAxes[0].nAxisId; 3253 nAxisIdy = maAxes[1].nAxisId; 3254 } 3255 else 3256 { 3257 nAxisIdx = lcl_generateRandomValue(); 3258 nAxisIdy = lcl_generateRandomValue(); 3259 AxesType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X; 3260 AxesType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y; 3261 maAxes.emplace_back( eXAxis, nAxisIdx, nAxisIdy ); 3262 maAxes.emplace_back( eYAxis, nAxisIdy, nAxisIdx ); 3263 } 3264 FSHelperPtr pFS = GetFS(); 3265 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx)); 3266 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy)); 3267 if (mbHasZAxis) 3268 { 3269 sal_Int32 nAxisIdz = 0; 3270 if( isDeep3dChart() ) 3271 { 3272 nAxisIdz = lcl_generateRandomValue(); 3273 maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy ); 3274 } 3275 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz)); 3276 } 3277 } 3278 3279 void ChartExport::exportGrouping( bool isBar ) 3280 { 3281 FSHelperPtr pFS = GetFS(); 3282 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY); 3283 // grouping 3284 if( GetProperty( xPropSet, "Stacked" ) ) 3285 mAny >>= mbStacked; 3286 if( GetProperty( xPropSet, "Percent" ) ) 3287 mAny >>= mbPercent; 3288 3289 const char* grouping = nullptr; 3290 if (mbStacked) 3291 grouping = "stacked"; 3292 else if (mbPercent) 3293 grouping = "percentStacked"; 3294 else 3295 { 3296 if( isBar && !isDeep3dChart() ) 3297 { 3298 grouping = "clustered"; 3299 } 3300 else 3301 grouping = "standard"; 3302 } 3303 pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, grouping); 3304 } 3305 3306 void ChartExport::exportTrendlines( const Reference< chart2::XDataSeries >& xSeries ) 3307 { 3308 FSHelperPtr pFS = GetFS(); 3309 Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, UNO_QUERY ); 3310 if( xRegressionCurveContainer.is() ) 3311 { 3312 Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves(); 3313 const Reference< chart2::XRegressionCurve >* pBeg = aRegCurveSeq.getConstArray(); 3314 const Reference< chart2::XRegressionCurve >* pEnd = pBeg + aRegCurveSeq.getLength(); 3315 for( const Reference< chart2::XRegressionCurve >* pIt = pBeg; pIt != pEnd; ++pIt ) 3316 { 3317 Reference< chart2::XRegressionCurve > xRegCurve = *pIt; 3318 if (!xRegCurve.is()) 3319 continue; 3320 3321 Reference< XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY ); 3322 3323 OUString aService; 3324 Reference< lang::XServiceName > xServiceName( xProperties, UNO_QUERY ); 3325 if( !xServiceName.is() ) 3326 continue; 3327 3328 aService = xServiceName->getServiceName(); 3329 3330 if(aService != "com.sun.star.chart2.LinearRegressionCurve" && 3331 aService != "com.sun.star.chart2.ExponentialRegressionCurve" && 3332 aService != "com.sun.star.chart2.LogarithmicRegressionCurve" && 3333 aService != "com.sun.star.chart2.PotentialRegressionCurve" && 3334 aService != "com.sun.star.chart2.PolynomialRegressionCurve" && 3335 aService != "com.sun.star.chart2.MovingAverageRegressionCurve") 3336 continue; 3337 3338 pFS->startElement(FSNS(XML_c, XML_trendline)); 3339 3340 OUString aName; 3341 xProperties->getPropertyValue("CurveName") >>= aName; 3342 if(!aName.isEmpty()) 3343 { 3344 pFS->startElement(FSNS(XML_c, XML_name)); 3345 pFS->writeEscaped(aName); 3346 pFS->endElement( FSNS( XML_c, XML_name) ); 3347 } 3348 3349 exportShapeProps( xProperties ); 3350 3351 if( aService == "com.sun.star.chart2.LinearRegressionCurve" ) 3352 { 3353 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "linear"); 3354 } 3355 else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" ) 3356 { 3357 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "exp"); 3358 } 3359 else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" ) 3360 { 3361 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "log"); 3362 } 3363 else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" ) 3364 { 3365 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "power"); 3366 } 3367 else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" ) 3368 { 3369 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "poly"); 3370 3371 sal_Int32 aDegree = 2; 3372 xProperties->getPropertyValue( "PolynomialDegree") >>= aDegree; 3373 pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(aDegree)); 3374 } 3375 else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" ) 3376 { 3377 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "movingAvg"); 3378 3379 sal_Int32 aPeriod = 2; 3380 xProperties->getPropertyValue( "MovingAveragePeriod") >>= aPeriod; 3381 3382 pFS->singleElement(FSNS(XML_c, XML_period), XML_val, OString::number(aPeriod)); 3383 } 3384 else 3385 { 3386 // should never happen 3387 // This would produce invalid OOXML files so we check earlier for the type 3388 assert(false); 3389 } 3390 3391 double fExtrapolateForward = 0.0; 3392 double fExtrapolateBackward = 0.0; 3393 3394 xProperties->getPropertyValue("ExtrapolateForward") >>= fExtrapolateForward; 3395 xProperties->getPropertyValue("ExtrapolateBackward") >>= fExtrapolateBackward; 3396 3397 pFS->singleElement( FSNS( XML_c, XML_forward ), 3398 XML_val, OString::number(fExtrapolateForward) ); 3399 3400 pFS->singleElement( FSNS( XML_c, XML_backward ), 3401 XML_val, OString::number(fExtrapolateBackward) ); 3402 3403 bool bForceIntercept = false; 3404 xProperties->getPropertyValue("ForceIntercept") >>= bForceIntercept; 3405 3406 if (bForceIntercept) 3407 { 3408 double fInterceptValue = 0.0; 3409 xProperties->getPropertyValue("InterceptValue") >>= fInterceptValue; 3410 3411 pFS->singleElement( FSNS( XML_c, XML_intercept ), 3412 XML_val, OString::number(fInterceptValue) ); 3413 } 3414 3415 // Equation properties 3416 Reference< XPropertySet > xEquationProperties( xRegCurve->getEquationProperties() ); 3417 3418 // Show Equation 3419 bool bShowEquation = false; 3420 xEquationProperties->getPropertyValue("ShowEquation") >>= bShowEquation; 3421 3422 // Show R^2 3423 bool bShowCorrelationCoefficient = false; 3424 xEquationProperties->getPropertyValue("ShowCorrelationCoefficient") >>= bShowCorrelationCoefficient; 3425 3426 pFS->singleElement( FSNS( XML_c, XML_dispRSqr ), 3427 XML_val, ToPsz10(bShowCorrelationCoefficient) ); 3428 3429 pFS->singleElement(FSNS(XML_c, XML_dispEq), XML_val, ToPsz10(bShowEquation)); 3430 3431 pFS->endElement( FSNS( XML_c, XML_trendline ) ); 3432 } 3433 } 3434 } 3435 3436 void ChartExport::exportMarker(const Reference< chart2::XDataSeries >& xSeries) 3437 { 3438 Reference< XPropertySet > xPropSet( xSeries, uno::UNO_QUERY ); 3439 chart2::Symbol aSymbol; 3440 if( GetProperty( xPropSet, "Symbol" ) ) 3441 mAny >>= aSymbol; 3442 3443 if(aSymbol.Style != chart2::SymbolStyle_STANDARD && aSymbol.Style != chart2::SymbolStyle_AUTO && aSymbol.Style != chart2::SymbolStyle_NONE) 3444 return; 3445 3446 FSHelperPtr pFS = GetFS(); 3447 pFS->startElement(FSNS(XML_c, XML_marker)); 3448 3449 sal_Int32 nSymbol = aSymbol.StandardSymbol; 3450 // TODO: more properties support for marker 3451 const char* pSymbolType; // no initialization here, to let compiler warn if we have a code path 3452 // where it stays uninitialized 3453 switch( nSymbol ) 3454 { 3455 case 0: 3456 pSymbolType = "square"; 3457 break; 3458 case 1: 3459 pSymbolType = "diamond"; 3460 break; 3461 case 2: 3462 case 3: 3463 case 4: 3464 case 5: 3465 pSymbolType = "triangle"; 3466 break; 3467 case 8: 3468 pSymbolType = "circle"; 3469 break; 3470 case 9: 3471 pSymbolType = "star"; 3472 break; 3473 case 10: 3474 pSymbolType = "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x' 3475 break; 3476 case 11: 3477 pSymbolType = "plus"; 3478 break; 3479 case 13: 3480 pSymbolType = "dash"; 3481 break; 3482 default: 3483 pSymbolType = "square"; 3484 break; 3485 } 3486 3487 bool bSkipFormatting = false; 3488 if (aSymbol.Style == chart2::SymbolStyle_NONE) 3489 { 3490 bSkipFormatting = true; 3491 pSymbolType = "none"; 3492 } 3493 3494 pFS->singleElement(FSNS(XML_c, XML_symbol), XML_val, pSymbolType); 3495 3496 if (!bSkipFormatting) 3497 { 3498 awt::Size aSymbolSize = aSymbol.Size; 3499 sal_Int32 nSize = std::max( aSymbolSize.Width, aSymbolSize.Height ); 3500 3501 nSize = nSize/250.0*7.0 + 1; // just guessed based on some test cases, 3502 //the value is always 1 less than the actual value. 3503 nSize = std::min<sal_Int32>( 72, std::max<sal_Int32>( 2, nSize ) ); 3504 pFS->singleElement(FSNS(XML_c, XML_size), XML_val, OString::number(nSize)); 3505 3506 pFS->startElement(FSNS(XML_c, XML_spPr)); 3507 3508 util::Color aColor = aSymbol.FillColor; 3509 if (GetProperty(xPropSet, "Color")) 3510 mAny >>= aColor; 3511 3512 if (aColor == -1) 3513 { 3514 pFS->singleElement(FSNS(XML_a, XML_noFill)); 3515 } 3516 else 3517 WriteSolidFill(::Color(aColor)); 3518 3519 pFS->endElement( FSNS( XML_c, XML_spPr ) ); 3520 } 3521 3522 pFS->endElement( FSNS( XML_c, XML_marker ) ); 3523 } 3524 3525 void ChartExport::exportSmooth() 3526 { 3527 FSHelperPtr pFS = GetFS(); 3528 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY ); 3529 sal_Int32 nSplineType = 0; 3530 if( GetProperty( xPropSet, "SplineType" ) ) 3531 mAny >>= nSplineType; 3532 const char* pVal = nSplineType != 0 ? "1" : "0"; 3533 pFS->singleElement(FSNS(XML_c, XML_smooth), XML_val, pVal); 3534 } 3535 3536 void ChartExport::exportFirstSliceAng( ) 3537 { 3538 FSHelperPtr pFS = GetFS(); 3539 sal_Int32 nStartingAngle = 0; 3540 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY); 3541 if( GetProperty( xPropSet, "StartingAngle" ) ) 3542 mAny >>= nStartingAngle; 3543 3544 // convert to ooxml angle 3545 nStartingAngle = (450 - nStartingAngle ) % 360; 3546 pFS->singleElement(FSNS(XML_c, XML_firstSliceAng), XML_val, OString::number(nStartingAngle)); 3547 } 3548 3549 namespace { 3550 3551 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle) 3552 { 3553 switch(nErrorBarStyle) 3554 { 3555 case cssc::ErrorBarStyle::NONE: 3556 return nullptr; 3557 case cssc::ErrorBarStyle::VARIANCE: 3558 break; 3559 case cssc::ErrorBarStyle::STANDARD_DEVIATION: 3560 return "stdDev"; 3561 case cssc::ErrorBarStyle::ABSOLUTE: 3562 return "fixedVal"; 3563 case cssc::ErrorBarStyle::RELATIVE: 3564 return "percentage"; 3565 case cssc::ErrorBarStyle::ERROR_MARGIN: 3566 break; 3567 case cssc::ErrorBarStyle::STANDARD_ERROR: 3568 return "stdErr"; 3569 case cssc::ErrorBarStyle::FROM_DATA: 3570 return "cust"; 3571 default: 3572 assert(false && "can't happen"); 3573 } 3574 return nullptr; 3575 } 3576 3577 Reference< chart2::data::XDataSequence> getLabeledSequence( 3578 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aSequences, 3579 bool bPositive ) 3580 { 3581 OUString aDirection; 3582 if(bPositive) 3583 aDirection = "positive"; 3584 else 3585 aDirection = "negative"; 3586 3587 for( sal_Int32 nI=0; nI< aSequences.getLength(); ++nI ) 3588 { 3589 if( aSequences[nI].is()) 3590 { 3591 uno::Reference< chart2::data::XDataSequence > xSequence( aSequences[nI]->getValues()); 3592 uno::Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW ); 3593 OUString aRole; 3594 if( ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) && 3595 aRole.match( "error-bars" ) && aRole.indexOf(aDirection) >= 0 ) 3596 { 3597 return xSequence; 3598 } 3599 } 3600 } 3601 3602 return Reference< chart2::data::XDataSequence > (); 3603 } 3604 3605 } 3606 3607 void ChartExport::exportErrorBar(const Reference< XPropertySet>& xErrorBarProps, bool bYError) 3608 { 3609 sal_Int32 nErrorBarStyle = cssc::ErrorBarStyle::NONE; 3610 xErrorBarProps->getPropertyValue("ErrorBarStyle") >>= nErrorBarStyle; 3611 const char* pErrorBarStyle = getErrorBarStyle(nErrorBarStyle); 3612 if(!pErrorBarStyle) 3613 return; 3614 3615 FSHelperPtr pFS = GetFS(); 3616 pFS->startElement(FSNS(XML_c, XML_errBars)); 3617 pFS->singleElement(FSNS(XML_c, XML_errDir), XML_val, bYError ? "y" : "x"); 3618 bool bPositive = false, bNegative = false; 3619 xErrorBarProps->getPropertyValue("ShowPositiveError") >>= bPositive; 3620 xErrorBarProps->getPropertyValue("ShowNegativeError") >>= bNegative; 3621 const char* pErrBarType; 3622 if(bPositive && bNegative) 3623 pErrBarType = "both"; 3624 else if(bPositive) 3625 pErrBarType = "plus"; 3626 else if(bNegative) 3627 pErrBarType = "minus"; 3628 else 3629 { 3630 // what the hell should we do now? 3631 // at least this makes the file valid 3632 pErrBarType = "both"; 3633 } 3634 pFS->singleElement(FSNS(XML_c, XML_errBarType), XML_val, pErrBarType); 3635 pFS->singleElement(FSNS(XML_c, XML_errValType), XML_val, pErrorBarStyle); 3636 pFS->singleElement(FSNS(XML_c, XML_noEndCap), XML_val, "0"); 3637 if(nErrorBarStyle == cssc::ErrorBarStyle::FROM_DATA) 3638 { 3639 uno::Reference< chart2::data::XDataSource > xDataSource(xErrorBarProps, uno::UNO_QUERY); 3640 Sequence< Reference < chart2::data::XLabeledDataSequence > > aSequences = 3641 xDataSource->getDataSequences(); 3642 3643 if(bPositive) 3644 { 3645 exportSeriesValues(getLabeledSequence(aSequences, true), XML_plus); 3646 } 3647 3648 if(bNegative) 3649 { 3650 exportSeriesValues(getLabeledSequence(aSequences, false), XML_minus); 3651 } 3652 } 3653 else 3654 { 3655 double nVal = 0.0; 3656 if(nErrorBarStyle == cssc::ErrorBarStyle::STANDARD_DEVIATION) 3657 { 3658 xErrorBarProps->getPropertyValue("Weight") >>= nVal; 3659 } 3660 else 3661 { 3662 if(bPositive) 3663 xErrorBarProps->getPropertyValue("PositiveError") >>= nVal; 3664 else 3665 xErrorBarProps->getPropertyValue("NegativeError") >>= nVal; 3666 } 3667 3668 pFS->singleElement(FSNS(XML_c, XML_val), XML_val, OString::number(nVal)); 3669 } 3670 3671 exportShapeProps( xErrorBarProps ); 3672 3673 pFS->endElement( FSNS( XML_c, XML_errBars) ); 3674 } 3675 3676 void ChartExport::exportView3D() 3677 { 3678 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY); 3679 if( !xPropSet.is() ) 3680 return; 3681 FSHelperPtr pFS = GetFS(); 3682 pFS->startElement(FSNS(XML_c, XML_view3D)); 3683 sal_Int32 eChartType = getChartType( ); 3684 // rotX 3685 if( GetProperty( xPropSet, "RotationHorizontal" ) ) 3686 { 3687 sal_Int32 nRotationX = 0; 3688 mAny >>= nRotationX; 3689 if( nRotationX < 0 ) 3690 { 3691 if(eChartType == chart::TYPEID_PIE) 3692 { 3693 /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range, 3694 so we conver that during import. It is modified in View3DConverter::convertFromModel() 3695 here we convert it back to 0..90 as we received in import */ 3696 nRotationX += 90; // X rotation (map Chart2 [-179,180] to OOXML [0..90]) 3697 } 3698 else 3699 nRotationX += 360; // X rotation (map Chart2 [-179,180] to OOXML [-90..90]) 3700 } 3701 pFS->singleElement(FSNS(XML_c, XML_rotX), XML_val, OString::number(nRotationX)); 3702 } 3703 // rotY 3704 if( GetProperty( xPropSet, "RotationVertical" ) ) 3705 { 3706 // Y rotation (map Chart2 [-179,180] to OOXML [0..359]) 3707 if( eChartType == chart::TYPEID_PIE && GetProperty( xPropSet, "StartingAngle" ) ) 3708 { 3709 // Y rotation used as 'first pie slice angle' in 3D pie charts 3710 sal_Int32 nStartingAngle=0; 3711 mAny >>= nStartingAngle; 3712 // convert to ooxml angle 3713 nStartingAngle = (450 - nStartingAngle ) % 360; 3714 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nStartingAngle)); 3715 } 3716 else 3717 { 3718 sal_Int32 nRotationY = 0; 3719 mAny >>= nRotationY; 3720 // Y rotation (map Chart2 [-179,180] to OOXML [0..359]) 3721 if( nRotationY < 0 ) 3722 nRotationY += 360; 3723 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nRotationY)); 3724 } 3725 } 3726 // rAngAx 3727 if( GetProperty( xPropSet, "RightAngledAxes" ) ) 3728 { 3729 bool bRightAngled = false; 3730 mAny >>= bRightAngled; 3731 const char* sRightAngled = bRightAngled ? "1":"0"; 3732 pFS->singleElement(FSNS(XML_c, XML_rAngAx), XML_val, sRightAngled); 3733 } 3734 // perspective 3735 if( GetProperty( xPropSet, "Perspective" ) ) 3736 { 3737 sal_Int32 nPerspective = 0; 3738 mAny >>= nPerspective; 3739 // map Chart2 [0,100] to OOXML [0..200] 3740 nPerspective *= 2; 3741 pFS->singleElement(FSNS(XML_c, XML_perspective), XML_val, OString::number(nPerspective)); 3742 } 3743 pFS->endElement( FSNS( XML_c, XML_view3D ) ); 3744 } 3745 3746 bool ChartExport::isDeep3dChart() 3747 { 3748 bool isDeep = false; 3749 if( mbIs3DChart ) 3750 { 3751 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY); 3752 if( GetProperty( xPropSet, "Deep" ) ) 3753 mAny >>= isDeep; 3754 } 3755 return isDeep; 3756 } 3757 3758 OUString ChartExport::getNumberFormatCode(sal_Int32 nKey) const 3759 { 3760 /* XXX if this was called more than one or two times per export the two 3761 * SvNumberFormatter instances and NfKeywordTable should be member 3762 * variables and initialized only once. */ 3763 3764 OUString aCode("General"); // init with fallback 3765 uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(mxChartModel, uno::UNO_QUERY_THROW); 3766 SvNumberFormatsSupplierObj* pSupplierObj = comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( xNumberFormatsSupplier); 3767 if (!pSupplierObj) 3768 return aCode; 3769 3770 SvNumberFormatter* pNumberFormatter = pSupplierObj->GetNumberFormatter(); 3771 if (!pNumberFormatter) 3772 return aCode; 3773 3774 SvNumberFormatter aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US); 3775 NfKeywordTable aKeywords; 3776 aTempFormatter.FillKeywordTableForExcel( aKeywords); 3777 aCode = pNumberFormatter->GetFormatStringForExcel( nKey, aKeywords, aTempFormatter); 3778 3779 return aCode; 3780 } 3781 3782 }// drawingml 3783 }// oox 3784 3785 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 3786
