/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace css; using namespace css::uno; namespace com::sun::star::chart2 { class XDataSeries; } namespace com::sun::star::chart2 { class XDiagram; } namespace com::sun::star::table { class XTableCharts; } namespace com::sun::star::table { class XTablePivotCharts; } class ChartTest : public test::BootstrapFixture, public unotest::MacrosTest { public: ChartTest():mbSkipValidation(false) {} void load( std::u16string_view rDir, const OUString& rFileName ); std::shared_ptr save( const OUString& rFileName ); std::shared_ptr reload( const OUString& rFileName ); uno::Sequence < OUString > getImpressChartColumnDescriptions( std::u16string_view pDir, const char* pName ); OUString getFileExtension( const OUString& rFileName ); uno::Reference< chart::XChartDocument > getChartDocFromImpress( std::u16string_view pDir, const char* pName ); uno::Reference getChartDocFromDrawImpress( sal_Int32 nPage, sal_Int32 nShape ); uno::Reference getChartDocFromWriter( sal_Int32 nShape ); awt::Size getPageSize( const Reference< chart2::XChartDocument > & xChartDoc ); awt::Size getSize(css::uno::Reference xDiagram, const awt::Size& rPageSize); virtual void setUp() override; virtual void tearDown() override; protected: Reference< lang::XComponent > mxComponent; OUString maServiceName; bool mbSkipValidation; // if you set this flag for a new test I'm going to haunt you! }; OUString ChartTest::getFileExtension( const OUString& aFileName ) { sal_Int32 nDotLocation = aFileName.lastIndexOf('.'); CPPUNIT_ASSERT(nDotLocation != -1); return aFileName.copy(nDotLocation+1); // Skip the dot. } void ChartTest::load( std::u16string_view aDir, const OUString& aName ) { OUString extension = getFileExtension(aName); if (extension == "ods" || extension == "xlsx" || extension == "fods") { maServiceName = "com.sun.star.sheet.SpreadsheetDocument"; } else if (extension == "docx") { maServiceName = "com.sun.star.text.TextDocument"; } else if (extension == "odg") { maServiceName = "com.sun.star.drawing.DrawingDocument"; } if (mxComponent.is()) mxComponent->dispose(); mxComponent = loadFromDesktop(m_directories.getURLFromSrc(aDir) + aName, maServiceName); } std::shared_ptr ChartTest::save(const OUString& rFilterName) { uno::Reference xStorable(mxComponent, uno::UNO_QUERY); auto aArgs(::comphelper::InitPropertySequence({ { "FilterName", Any(rFilterName) } })); std::shared_ptr pTempFile = std::make_shared(); pTempFile->EnableKillingFile(); xStorable->storeToURL(pTempFile->GetURL(), aArgs); return pTempFile; } std::shared_ptr ChartTest::reload(const OUString& rFilterName) { std::shared_ptr pTempFile = save(rFilterName); mxComponent->dispose(); mxComponent = loadFromDesktop(pTempFile->GetURL(), maServiceName); std::cout << pTempFile->GetURL(); if(rFilterName == "Calc Office Open XML") { validate(pTempFile->GetFileName(), test::OOXML); } else if(rFilterName == "Office Open XML Text") { // validate(pTempFile->GetFileName(), test::OOXML); } else if(rFilterName == "calc8") { if(!mbSkipValidation) validate(pTempFile->GetFileName(), test::ODF); } else if(rFilterName == "MS Excel 97") { if(!mbSkipValidation) validate(pTempFile->GetFileName(), test::MSBINARY); } return pTempFile; } void ChartTest::setUp() { test::BootstrapFixture::setUp(); mxDesktop.set( css::frame::Desktop::create( comphelper::getComponentContext(getMultiServiceFactory()) ) ); } void ChartTest::tearDown() { if(mxComponent.is()) mxComponent->dispose(); test::BootstrapFixture::tearDown(); } Reference< lang::XComponent > getChartCompFromSheet( sal_Int32 nSheet, uno::Reference< lang::XComponent > const & xComponent ) { // let us assume that we only have one chart per sheet uno::Reference< sheet::XSpreadsheetDocument > xDoc(xComponent, UNO_QUERY_THROW); uno::Reference< container::XIndexAccess > xIA(xDoc->getSheets(), UNO_QUERY_THROW); uno::Reference< table::XTableChartsSupplier > xChartSupplier( xIA->getByIndex(nSheet), UNO_QUERY_THROW); uno::Reference< table::XTableCharts > xCharts = xChartSupplier->getCharts(); CPPUNIT_ASSERT(xCharts.is()); uno::Reference< container::XIndexAccess > xIACharts(xCharts, UNO_QUERY_THROW); uno::Reference< table::XTableChart > xChart( xIACharts->getByIndex(0), UNO_QUERY_THROW); uno::Reference< document::XEmbeddedObjectSupplier > xEmbObjectSupplier(xChart, UNO_QUERY_THROW); uno::Reference< lang::XComponent > xChartComp( xEmbObjectSupplier->getEmbeddedObject(), UNO_SET_THROW ); return xChartComp; } Reference< chart2::XChartDocument > getChartDocFromSheet( sal_Int32 nSheet, uno::Reference< lang::XComponent > const & xComponent ) { uno::Reference< chart2::XChartDocument > xChartDoc ( getChartCompFromSheet(nSheet, xComponent), UNO_QUERY_THROW ); // Update the chart view, so that its draw page is updated and ready for the test css::uno::Reference xModel(xChartDoc, css::uno::UNO_QUERY_THROW); ChartHelper::updateChart(xModel); return xChartDoc; } uno::Reference getTablePivotChartsFromSheet(sal_Int32 nSheet, uno::Reference const & xComponent) { uno::Reference xDoc(xComponent, UNO_QUERY_THROW); uno::Reference xIA(xDoc->getSheets(), UNO_QUERY_THROW); uno::Reference xChartSupplier(xIA->getByIndex(nSheet), UNO_QUERY_THROW); uno::Reference xTablePivotCharts = xChartSupplier->getPivotCharts(); CPPUNIT_ASSERT(xTablePivotCharts.is()); return xTablePivotCharts; } Reference getPivotChartCompFromSheet(sal_Int32 nSheet, uno::Reference const & xComponent) { uno::Reference xTablePivotCharts = getTablePivotChartsFromSheet(nSheet, xComponent); uno::Reference xIACharts(xTablePivotCharts, UNO_QUERY_THROW); uno::Reference xTablePivotChart(xIACharts->getByIndex(0), UNO_QUERY_THROW); uno::Reference xEmbObjectSupplier(xTablePivotChart, UNO_QUERY_THROW); uno::Reference xChartComp(xEmbObjectSupplier->getEmbeddedObject(), UNO_SET_THROW); return xChartComp; } Reference getPivotChartDocFromSheet(sal_Int32 nSheet, uno::Reference const & xComponent) { uno::Reference xChartDoc(getPivotChartCompFromSheet(nSheet, xComponent), UNO_QUERY_THROW); return xChartDoc; } Reference getPivotChartDocFromSheet(uno::Reference const & xTablePivotCharts, sal_Int32 nIndex) { uno::Reference xIACharts(xTablePivotCharts, UNO_QUERY_THROW); uno::Reference xTablePivotChart(xIACharts->getByIndex(nIndex), UNO_QUERY_THROW); uno::Reference xEmbObjectSupplier(xTablePivotChart, UNO_QUERY_THROW); uno::Reference xChartComp(xEmbObjectSupplier->getEmbeddedObject(), UNO_SET_THROW); uno::Reference xChartDoc(xChartComp, UNO_QUERY_THROW); return xChartDoc; } Reference< chart2::XChartType > getChartTypeFromDoc( Reference< chart2::XChartDocument > const & xChartDoc, sal_Int32 nChartType, sal_Int32 nCooSys = 0 ) { CPPUNIT_ASSERT( xChartDoc.is() ); Reference xDiagram = xChartDoc->getFirstDiagram(); CPPUNIT_ASSERT( xDiagram.is() ); Reference< chart2::XCoordinateSystemContainer > xCooSysContainer( xDiagram, UNO_QUERY_THROW ); Sequence< Reference< chart2::XCoordinateSystem > > xCooSysSequence( xCooSysContainer->getCoordinateSystems()); CPPUNIT_ASSERT( xCooSysSequence.getLength() > nCooSys ); Reference< chart2::XChartTypeContainer > xChartTypeContainer( xCooSysSequence[nCooSys], UNO_QUERY_THROW ); Sequence< Reference< chart2::XChartType > > xChartTypeSequence( xChartTypeContainer->getChartTypes() ); CPPUNIT_ASSERT( xChartTypeSequence.getLength() > nChartType ); return xChartTypeSequence[nChartType]; } Reference getAxisFromDoc( const Reference& xChartDoc, sal_Int32 nCooSys, sal_Int32 nAxisDim, sal_Int32 nAxisIndex ) { Reference xDiagram = xChartDoc->getFirstDiagram(); CPPUNIT_ASSERT(xDiagram.is()); Reference xCooSysContainer(xDiagram, UNO_QUERY_THROW); Sequence > xCooSysSequence = xCooSysContainer->getCoordinateSystems(); CPPUNIT_ASSERT(xCooSysSequence.getLength() > nCooSys); Reference xCoord = xCooSysSequence[nCooSys]; CPPUNIT_ASSERT(xCoord.is()); Reference xAxis = xCoord->getAxisByDimension(nAxisDim, nAxisIndex); CPPUNIT_ASSERT(xAxis.is()); return xAxis; } sal_Int32 getNumberOfDataSeries(uno::Reference const & xChartDoc, sal_Int32 nChartType = 0, sal_Int32 nCooSys = 0) { Reference xChartType = getChartTypeFromDoc(xChartDoc, nChartType, nCooSys); Reference xDataSeriesContainer(xChartType, UNO_QUERY_THROW); uno::Sequence> xSeriesSequence(xDataSeriesContainer->getDataSeries()); return xSeriesSequence.getLength(); } Reference< chart2::XDataSeries > getDataSeriesFromDoc(uno::Reference const & xChartDoc, sal_Int32 nDataSeries, sal_Int32 nChartType = 0, sal_Int32 nCooSys = 0) { Reference< chart2::XChartType > xChartType = getChartTypeFromDoc( xChartDoc, nChartType, nCooSys ); Reference< chart2::XDataSeriesContainer > xDataSeriesContainer( xChartType, UNO_QUERY_THROW ); Sequence< Reference< chart2::XDataSeries > > xSeriesSequence( xDataSeriesContainer->getDataSeries() ); CPPUNIT_ASSERT( xSeriesSequence.getLength() > nDataSeries ); Reference< chart2::XDataSeries > xSeries = xSeriesSequence[nDataSeries]; return xSeries; } Reference< chart2::data::XDataSequence > getLabelDataSequenceFromDoc( Reference< chart2::XChartDocument > const & xChartDoc, sal_Int32 nDataSeries = 0, sal_Int32 nChartType = 0 ) { Reference< chart2::XDataSeries > xDataSeries = getDataSeriesFromDoc( xChartDoc, nDataSeries, nChartType ); CPPUNIT_ASSERT(xDataSeries.is()); Reference< chart2::data::XDataSource > xDataSource( xDataSeries, uno::UNO_QUERY_THROW ); const Sequence< Reference< chart2::data::XLabeledDataSequence > > xDataSequences = xDataSource->getDataSequences(); for(auto const & lds : xDataSequences) { Reference< chart2::data::XDataSequence> xLabelSeq = lds->getLabel(); if(!xLabelSeq.is()) continue; return xLabelSeq; } CPPUNIT_FAIL("no Label sequence found"); } Reference< chart2::data::XDataSequence > getDataSequenceFromDocByRole( Reference< chart2::XChartDocument > const & xChartDoc, std::u16string_view rRole, sal_Int32 nDataSeries = 0, sal_Int32 nChartType = 0 ) { Reference< chart2::XDataSeries > xDataSeries = getDataSeriesFromDoc( xChartDoc, nDataSeries, nChartType ); CPPUNIT_ASSERT(xDataSeries.is()); Reference< chart2::data::XDataSource > xDataSource( xDataSeries, uno::UNO_QUERY_THROW ); const Sequence< Reference< chart2::data::XLabeledDataSequence > > xDataSequences = xDataSource->getDataSequences(); for(auto const & lds : xDataSequences) { Reference< chart2::data::XDataSequence> xLabelSeq = lds->getValues(); uno::Reference< beans::XPropertySet > xProps(xLabelSeq, uno::UNO_QUERY); if(!xProps.is()) continue; OUString aRoleName = xProps->getPropertyValue("Role").get(); if(aRoleName == rRole) return xLabelSeq; } return Reference< chart2::data::XDataSequence > (); } uno::Sequence < OUString > getWriterChartColumnDescriptions( Reference< lang::XComponent > const & mxComponent ) { uno::Reference xDrawPageSupplier(mxComponent, uno::UNO_QUERY); uno::Reference xDrawPage = xDrawPageSupplier->getDrawPage(); uno::Reference xShape(xDrawPage->getByIndex(0), uno::UNO_QUERY); CPPUNIT_ASSERT( xShape.is() ); uno::Reference xPropertySet(xShape, uno::UNO_QUERY); uno::Reference< chart2::XChartDocument > xChartDoc; xChartDoc.set( xPropertySet->getPropertyValue( "Model" ), uno::UNO_QUERY ); CPPUNIT_ASSERT( xChartDoc.is() ); CPPUNIT_ASSERT( xChartDoc->getDataProvider().is() ); uno::Reference< chart2::XAnyDescriptionAccess > xAnyDescriptionAccess ( xChartDoc->getDataProvider(), uno::UNO_QUERY_THROW ); uno::Sequence< OUString > seriesList = xAnyDescriptionAccess->getColumnDescriptions(); return seriesList; } std::vector > getDataSeriesYValuesFromChartType( const Reference& xCT ) { Reference xDSCont(xCT, uno::UNO_QUERY); CPPUNIT_ASSERT(xDSCont.is()); const Sequence > aDataSeriesSeq = xDSCont->getDataSeries(); double fNan; rtl::math::setNan(&fNan); std::vector > aRet; for (uno::Reference const & ds : aDataSeriesSeq) { uno::Reference xDSrc(ds, uno::UNO_QUERY); CPPUNIT_ASSERT(xDSrc.is()); const uno::Sequence > aDataSeqs = xDSrc->getDataSequences(); for (auto const & lds : aDataSeqs) { Reference xValues = lds->getValues(); CPPUNIT_ASSERT(xValues.is()); Reference xPropSet(xValues, uno::UNO_QUERY); if (!xPropSet.is()) continue; OUString aRoleName; xPropSet->getPropertyValue("Role") >>= aRoleName; if (aRoleName == "values-y") { const uno::Sequence aData = xValues->getData(); std::vector aValues; aValues.reserve(aData.getLength()); for (uno::Any const & any : aData) { double fVal; if (any >>= fVal) aValues.push_back(fVal); else aValues.push_back(fNan); } aRet.push_back(aValues); } } } return aRet; } std::vector > getDataSeriesLabelsFromChartType( const Reference& xCT ) { OUString aLabelRole = xCT->getRoleOfSequenceForSeriesLabel(); Reference xDSCont(xCT, uno::UNO_QUERY); CPPUNIT_ASSERT(xDSCont.is()); const Sequence > aDataSeriesSeq = xDSCont->getDataSeries(); std::vector > aRet; for (auto const & ds : aDataSeriesSeq) { uno::Reference xDSrc(ds, uno::UNO_QUERY); CPPUNIT_ASSERT(xDSrc.is()); const uno::Sequence > aDataSeqs = xDSrc->getDataSequences(); for (auto const & lds : aDataSeqs) { Reference xValues = lds->getValues(); CPPUNIT_ASSERT(xValues.is()); Reference xPropSet(xValues, uno::UNO_QUERY); if (!xPropSet.is()) continue; OUString aRoleName; xPropSet->getPropertyValue("Role") >>= aRoleName; if (aRoleName == aLabelRole) { Reference xLabel = lds; CPPUNIT_ASSERT(xLabel.is()); Reference xDS2 = xLabel->getLabel(); CPPUNIT_ASSERT(xDS2.is()); uno::Sequence aData = xDS2->getData(); aRet.push_back(aData); } } } return aRet; } uno::Reference< chart::XChartDocument > ChartTest::getChartDocFromImpress( std::u16string_view pDir, const char* pName ) { mxComponent = loadFromDesktop(m_directories.getURLFromSrc(pDir) + OUString::createFromAscii(pName), "com.sun.star.comp.Draw.PresentationDocument"); uno::Reference< drawing::XDrawPagesSupplier > xDoc(mxComponent, uno::UNO_QUERY_THROW ); uno::Reference< drawing::XDrawPage > xPage( xDoc->getDrawPages()->getByIndex(0), uno::UNO_QUERY_THROW ); uno::Reference< beans::XPropertySet > xShapeProps( xPage->getByIndex(0), uno::UNO_QUERY ); CPPUNIT_ASSERT(xShapeProps.is()); uno::Reference< frame::XModel > xDocModel; xShapeProps->getPropertyValue("Model") >>= xDocModel; CPPUNIT_ASSERT(xDocModel.is()); uno::Reference< chart::XChartDocument > xChartDoc( xDocModel, uno::UNO_QUERY_THROW ); return xChartDoc; } uno::Reference ChartTest::getChartDocFromDrawImpress( sal_Int32 nPage, sal_Int32 nShape ) { uno::Reference xEmpty; uno::Reference xPages(mxComponent, uno::UNO_QUERY); if (!xPages.is()) return xEmpty; uno::Reference xPage( xPages->getDrawPages()->getByIndex(nPage), uno::UNO_QUERY_THROW); uno::Reference xShapeProps(xPage->getByIndex(nShape), uno::UNO_QUERY); if (!xShapeProps.is()) return xEmpty; uno::Reference xDocModel; xShapeProps->getPropertyValue("Model") >>= xDocModel; if (!xDocModel.is()) return xEmpty; uno::Reference xChartDoc(xDocModel, uno::UNO_QUERY); return xChartDoc; } uno::Reference ChartTest::getChartDocFromWriter( sal_Int32 nShape ) { // DO NOT use XDrawPageSupplier since SwVirtFlyDrawObj are not created // during import, only in layout! Reference xEOS(mxComponent, uno::UNO_QUERY); CPPUNIT_ASSERT(xEOS.is()); Reference xEmbeddeds(xEOS->getEmbeddedObjects(), uno::UNO_QUERY); CPPUNIT_ASSERT(xEmbeddeds.is()); Reference xShapeProps(xEmbeddeds->getByIndex(nShape), uno::UNO_QUERY); CPPUNIT_ASSERT(xShapeProps.is()); Reference xDocModel; xShapeProps->getPropertyValue("Model") >>= xDocModel; CPPUNIT_ASSERT(xDocModel.is()); uno::Reference xChartDoc(xDocModel, uno::UNO_QUERY); return xChartDoc; } uno::Sequence < OUString > ChartTest::getImpressChartColumnDescriptions( std::u16string_view pDir, const char* pName ) { uno::Reference< chart::XChartDocument > xChartDoc = getChartDocFromImpress( pDir, pName ); uno::Reference< chart::XChartDataArray > xChartData ( xChartDoc->getData(), uno::UNO_QUERY_THROW); uno::Sequence < OUString > seriesList = xChartData->getColumnDescriptions(); return seriesList; } OUString getTitleString( const Reference& xTitled ) { uno::Reference xTitle = xTitled->getTitleObject(); CPPUNIT_ASSERT(xTitle.is()); const uno::Sequence > aFSSeq = xTitle->getText(); OUString aText; for (auto const & fs : aFSSeq) aText += fs->getString(); return aText; } sal_Int32 getNumberFormat( const Reference& xChartDoc, const OUString& sFormat ) { Reference xNFS(xChartDoc, uno::UNO_QUERY_THROW); Reference xNumberFormats = xNFS->getNumberFormats(); CPPUNIT_ASSERT(xNumberFormats.is()); return xNumberFormats->queryKey(sFormat, css::lang::Locale(), false); } sal_Int32 getNumberFormatFromAxis( const Reference& xAxis ) { Reference xPS(xAxis, uno::UNO_QUERY); CPPUNIT_ASSERT(xPS.is()); sal_Int32 nNumberFormat = -1; bool bSuccess = xPS->getPropertyValue(CHART_UNONAME_NUMFMT) >>= nNumberFormat; CPPUNIT_ASSERT(bSuccess); return nNumberFormat; } sal_Int16 getNumberFormatType( const Reference& xChartDoc, sal_Int32 nNumberFormat ) { Reference xNFS(xChartDoc, uno::UNO_QUERY_THROW); Reference xNumberFormats = xNFS->getNumberFormats(); CPPUNIT_ASSERT(xNumberFormats.is()); Reference xNumPS = xNumberFormats->getByKey(nNumberFormat); CPPUNIT_ASSERT(xNumPS.is()); sal_Int16 nType = util::NumberFormat::UNDEFINED; xNumPS->getPropertyValue("Type") >>= nType; return nType; } awt::Size ChartTest::getPageSize( const Reference< chart2::XChartDocument > & xChartDoc ) { awt::Size aSize( 0, 0 ); uno::Reference< com::sun::star::embed::XVisualObject > xVisualObject( xChartDoc, uno::UNO_QUERY ); CPPUNIT_ASSERT( xVisualObject.is() ); aSize = xVisualObject->getVisualAreaSize( com::sun::star::embed::Aspects::MSOLE_CONTENT ); return aSize; } awt::Size ChartTest::getSize(css::uno::Reference xDiagram, const awt::Size& rPageSize) { Reference< beans::XPropertySet > xProp(xDiagram, uno::UNO_QUERY); chart2::RelativeSize aRelativeSize; xProp->getPropertyValue( "RelativeSize" ) >>= aRelativeSize; double fX = aRelativeSize.Primary * rPageSize.Width; double fY = aRelativeSize.Secondary * rPageSize.Height; awt::Size aSize; aSize.Width = static_cast< sal_Int32 >( ::rtl::math::round( fX ) ); aSize.Height = static_cast< sal_Int32 >( ::rtl::math::round( fY ) ); return aSize; } uno::Reference getShapeByName(const uno::Reference& rShapes, const OUString& rName, const std::function&)>& pCondition = nullptr) { for (sal_Int32 i = 0; i < rShapes->getCount(); ++i) { uno::Reference xShapes(rShapes->getByIndex(i), uno::UNO_QUERY); if (xShapes.is()) { uno::Reference xRet = getShapeByName(xShapes, rName, pCondition); if (xRet.is()) return xRet; } uno::Reference xNamedShape(rShapes->getByIndex(i), uno::UNO_QUERY); if (xNamedShape->getName() == rName) { uno::Reference xShape(xNamedShape, uno::UNO_QUERY); if (pCondition == nullptr || pCondition(xShape)) return xShape; } } return uno::Reference(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */