/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <config_vclplug.h>

#include "charttest.hxx"
#include <com/sun/star/chart2/DataPointLabel.hpp>
#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
#include <com/sun/star/chart2/RelativePosition.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/chart/XChartDocument.hpp>
#include <com/sun/star/chart2/XInternalDataProvider.hpp>
#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
#include <com/sun/star/chart/XAxisXSupplier.hpp>
#include <com/sun/star/chart/DataLabelPlacement.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>

using namespace css;
using namespace css::uno;
using namespace ::cpo::uno;

class Chart2ImportTest2 : public ChartTest
{
public:
    Chart2ImportTest2()
        : ChartTest(u"/chart2/qa/extras/data/"_ustr)
    {
    }
};

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf114179)
{
    loadFromFile(u"docx/testTdf114179.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());
    css::uno::Reference<chart2::XDiagram> xDiagram;
    xDiagram.set(xChartDoc->getFirstDiagram());
    CPPUNIT_ASSERT_MESSAGE("There is a Diagram.", xDiagram.is());
    awt::Size aPage(0, 0);
    uno::Reference<css::embed::XVisualObject> xVisualObject(xChartDoc, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xVisualObject.is());
    aPage = xVisualObject->getVisualAreaSize(css::embed::Aspects::MSOLE_CONTENT);
    Reference<beans::XPropertySet> xProp(xDiagram, uno::UNO_QUERY);
    chart2::RelativeSize aRelativeSize;
    xProp->getPropertyValue(u"RelativeSize"_ustr) >>= aRelativeSize;
    double fX = aRelativeSize.Primary * aPage.Width;
    double fY = aRelativeSize.Secondary * aPage.Height;
    CPPUNIT_ASSERT(static_cast<sal_Int32>(::rtl::math::round(fX)) > 0);
    CPPUNIT_ASSERT(static_cast<sal_Int32>(::rtl::math::round(fY)) > 0);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf124243)
{
    loadFromFile(u"docx/tdf124243.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xAxis.is());

    Reference<beans::XPropertySet> xPS(xAxis, uno::UNO_QUERY_THROW);
    bool bShow = true;
    // test X Axis is not visible.
    bool bSuccess = xPS->getPropertyValue(u"Show"_ustr) >>= bShow;
    CPPUNIT_ASSERT(bSuccess);
    CPPUNIT_ASSERT(!bShow);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf127393)
{
    loadFromFile(u"pptx/tdf127393.pptx");

    // 1st chart
    Reference<chart2::XChartDocument> xChartDoc(getChartDocFromDrawImpress(0, 0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData1 = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData1.Categories.is());
    CPPUNIT_ASSERT(aScaleData1.ShiftedCategoryPosition);

    // 2nd chart
    xChartDoc.set(getChartDocFromDrawImpress(1, 0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    xAxis.set(getAxisFromDoc(xChartDoc, 0, 0, 0));
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData2 = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData2.Categories.is());
    CPPUNIT_ASSERT(!aScaleData2.ShiftedCategoryPosition);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf128733)
{
    loadFromFile(u"odt/tdf128733.odt");

    Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());
    // test secondary X axis ShiftedCategoryPosition value
    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 1);
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());
    CPPUNIT_ASSERT(aScaleData.ShiftedCategoryPosition);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf128432)
{
    loadFromFile(u"ods/tdf128432.ods");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());
    CPPUNIT_ASSERT(aScaleData.ShiftedCategoryPosition);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf128627)
{
    loadFromFile(u"xlsx/tdf128627.xlsx");
    // Test ShiftedCategoryPosition for Radar Chart
    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());
    CPPUNIT_ASSERT(!aScaleData.ShiftedCategoryPosition);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf128634)
{
    loadFromFile(u"xlsx/tdf128634.xlsx");
    // Test ShiftedCategoryPosition for 3D Charts
    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());
    CPPUNIT_ASSERT(aScaleData.ShiftedCategoryPosition);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf130657)
{
    loadFromFile(u"xlsx/tdf130657.xlsx");
    // Test ShiftedCategoryPosition for charts which is not contain a "crossbetween" OOXML tag.
    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());
    CPPUNIT_ASSERT(aScaleData.ShiftedCategoryPosition);
}

namespace
{
void checkDataLabelProperties(const Reference<chart2::XDataSeries>& xDataSeries,
                              sal_Int32 nDataPointIndex, bool bValueVisible)
{
    uno::Reference<beans::XPropertySet> xPropertySet(
        xDataSeries->getDataPointByIndex(nDataPointIndex), uno::UNO_SET_THROW);
    chart2::DataPointLabel aLabel;
    xPropertySet->getPropertyValue(u"Label"_ustr) >>= aLabel;
    CPPUNIT_ASSERT_EQUAL(bValueVisible, aLabel.ShowNumber);
    CPPUNIT_ASSERT_EQUAL(false, aLabel.ShowNumberInPercent);
}
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testDeletedDataLabel)
{
    loadFromFile(u"xlsx/deleted_data_labels.xlsx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartCompFromSheet(0, 0), UNO_QUERY_THROW);
    Reference<chart2::XDataSeries> xDataSeries0 = getDataSeriesFromDoc(xChartDoc, 0);
    CPPUNIT_ASSERT(xDataSeries0.is());
    checkDataLabelProperties(xDataSeries0, 0, true);
    checkDataLabelProperties(xDataSeries0, 1, false);
    checkDataLabelProperties(xDataSeries0, 2, true);
    Reference<chart2::XDataSeries> xDataSeries1 = getDataSeriesFromDoc(xChartDoc, 1);
    CPPUNIT_ASSERT(xDataSeries1.is());
    checkDataLabelProperties(xDataSeries1, 0, false);
    checkDataLabelProperties(xDataSeries1, 1, false);
    checkDataLabelProperties(xDataSeries1, 2, false);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testDataPointInheritedColorDOCX)
{
    loadFromFile(u"docx/data_point_inherited_color.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());
    css::uno::Reference<chart2::XDiagram> xDiagram(xChartDoc->getFirstDiagram(), UNO_SET_THROW);

    Reference<chart2::XDataSeries> xDataSeries = getDataSeriesFromDoc(xChartDoc, 0);
    uno::Reference<beans::XPropertySet> xPropertySet(xDataSeries->getDataPointByIndex(0),
                                                     uno::UNO_SET_THROW);
    CPPUNIT_ASSERT(xPropertySet.is());
    sal_Int32 nColor = xPropertySet->getPropertyValue(u"FillColor"_ustr).get<sal_Int32>();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(16776960), nColor);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testExternalStrRefsXLSX)
{
    loadFromFile(u"xlsx/external_str_ref.xlsx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartCompFromSheet(0, 0), UNO_QUERY_THROW);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    chart2::ScaleData aScaleData = xAxis->getScaleData();
    cpo::uno::Sequence<cpo::uno::Any> aValues = aScaleData.Categories->getValues()->getData();
    CPPUNIT_ASSERT_EQUAL(u"test1"_ustr, aValues[0].get<OUString>());
    CPPUNIT_ASSERT_EQUAL(u"test2"_ustr, aValues[1].get<OUString>());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testSourceNumberFormatComplexCategoriesXLS)
{
    loadFromFile(u"xls/source_number_format_axis.xls");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartCompFromSheet(0, 0), UNO_QUERY_THROW);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    chart2::ScaleData aScaleData = xAxis->getScaleData();
    sal_Int32 nNumberFormat = aScaleData.Categories->getValues()->getNumberFormatKeyByIndex(-1);
    CPPUNIT_ASSERT(nNumberFormat != 0);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testSimpleCategoryAxis)
{
    loadFromFile(u"docx/testSimpleCategoryAxis.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    // Test the internal data.
    CPPUNIT_ASSERT(xChartDoc->hasInternalDataProvider());

    Reference<chart2::XInternalDataProvider> xInternalProvider(xChartDoc->getDataProvider(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xInternalProvider.is());

    Reference<chart::XComplexDescriptionAccess> xDescAccess(xInternalProvider, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xDescAccess.is());

    // Get the category labels.
    Sequence<Sequence<OUString>> aCategories = xDescAccess->getComplexRowDescriptions();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aCategories[0].getLength());
    CPPUNIT_ASSERT(aCategories[0][0].endsWith("ria 1"));
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aCategories[1].getLength());
    CPPUNIT_ASSERT(aCategories[1][0].endsWith("ria 2"));
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aCategories[2].getLength());
    CPPUNIT_ASSERT(aCategories[2][0].endsWith("ria 3"));
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aCategories[3].getLength());
    CPPUNIT_ASSERT(aCategories[3][0].endsWith("ria 4"));
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testMultilevelCategoryAxis)
{
    loadFromFile(u"docx/testMultilevelCategoryAxis.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    // Test the internal data.
    CPPUNIT_ASSERT(xChartDoc->hasInternalDataProvider());

    Reference<chart2::XInternalDataProvider> xInternalProvider(xChartDoc->getDataProvider(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xInternalProvider.is());

    Reference<chart::XComplexDescriptionAccess> xDescAccess(xInternalProvider, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xDescAccess.is());

    // Get the complex category labels.
    Sequence<Sequence<OUString>> aCategories = xDescAccess->getComplexRowDescriptions();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aCategories.getLength());
    CPPUNIT_ASSERT_EQUAL(u"2011"_ustr, aCategories[0][0]);
    CPPUNIT_ASSERT_EQUAL(u""_ustr, aCategories[1][0]);
    CPPUNIT_ASSERT_EQUAL(u"2012"_ustr, aCategories[2][0]);
    CPPUNIT_ASSERT_EQUAL(u""_ustr, aCategories[3][0]);
    CPPUNIT_ASSERT_EQUAL(u"Categoria 1"_ustr, aCategories[0][1]);
    CPPUNIT_ASSERT_EQUAL(u"Categoria 2"_ustr, aCategories[1][1]);
    CPPUNIT_ASSERT_EQUAL(u"Categoria 3"_ustr, aCategories[2][1]);
    CPPUNIT_ASSERT_EQUAL(u"Categoria 4"_ustr, aCategories[3][1]);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testXaxisValues)
{
    loadFromFile(u"docx/tdf124083.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    const uno::Reference<chart2::data::XDataSequence> xDataSeq
        = getDataSequenceFromDocByRole(xChartDoc, u"values-x");
    Sequence<cpo::uno::Any> xSequence = xDataSeq->getData();
    // test X values
    CPPUNIT_ASSERT_EQUAL(cpo::uno::Any(0.04), xSequence[0]);
    CPPUNIT_ASSERT(std::isnan(*static_cast<const double*>(xSequence[1].getValue())));
    CPPUNIT_ASSERT_EQUAL(cpo::uno::Any(0.16), xSequence[2]);
    CPPUNIT_ASSERT_EQUAL(cpo::uno::Any(0.11), xSequence[3]);
    CPPUNIT_ASSERT(std::isnan(*static_cast<const double*>(xSequence[4].getValue())));
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf123504)
{
    loadFromFile(u"ods/pie_chart_100_and_0.ods");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);

    Reference<chart2::XChartDocument> xChartDoc2(xChartDoc, UNO_QUERY_THROW);
    Reference<chart2::XChartType> xChartType(getChartTypeFromDoc(xChartDoc2, 0), UNO_SET_THROW);
    std::vector aDataSeriesYValues = getDataSeriesYValuesFromChartType(xChartType);
    CPPUNIT_ASSERT_EQUAL(size_t(1), aDataSeriesYValues.size());

    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xSeriesSlices(
        getShapeByName(xShapes, u"CID/D=0:CS=0:CT=0:Series=0"_ustr), UNO_SET_THROW);

    Reference<container::XIndexAccess> xIndexAccess(xSeriesSlices, UNO_QUERY_THROW);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xIndexAccess->getCount());
    Reference<drawing::XShape> xSlice(xIndexAccess->getByIndex(0), UNO_QUERY_THROW);

    // Check size and position of the only slice in the chart (100%)
    // In the regressed state, it used to be 0-sized at position 0,0
    awt::Point aSlicePosition = xSlice->getPosition();
    CPPUNIT_ASSERT_GREATER(sal_Int32(3000), aSlicePosition.X);
    CPPUNIT_ASSERT_GREATER(sal_Int32(150), aSlicePosition.Y);
    awt::Size aSliceSize = xSlice->getSize();
    CPPUNIT_ASSERT_DOUBLES_EQUAL(8300.0, aSliceSize.Height, 10);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(8300.0, aSliceSize.Width, 10);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf122765)
{
    // The horizontal position of the slices was wrong.
    loadFromFile(u"pptx/tdf122765.pptx");
    Reference<chart::XChartDocument> xChartDoc = getChartDocFromDrawImpress(0, 0);
    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xSeriesSlices(
        getShapeByName(xShapes, u"CID/D=0:CS=0:CT=0:Series=0"_ustr), UNO_SET_THROW);

    Reference<container::XIndexAccess> xIndexAccess(xSeriesSlices, UNO_QUERY_THROW);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(9), xIndexAccess->getCount());
    Reference<drawing::XShape> xSlice(xIndexAccess->getByIndex(0), UNO_QUERY_THROW);

    // Check position of the first slice, all slices move together, so enough to check only one.
    // Wrong position was around 5856.
    awt::Point aSlicePosition = xSlice->getPosition();
    CPPUNIT_ASSERT_GREATER(sal_Int32(7000), aSlicePosition.X);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf123206CustomLabelField)
{
    // File contains the deprecated "custom-label-field" attribute of the
    // "data-point" element. It should be interpreted and stored as a data point
    // property.
    loadFromFile(u"odp/tdf123206.odp");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromDrawImpress(0, 0),
                                                     uno::UNO_QUERY_THROW);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());
    CPPUNIT_ASSERT(xChartDoc.is());
    Reference<chart2::XDataSeries> xDataSeries = getDataSeriesFromDoc(xChartDoc, 0);
    CPPUNIT_ASSERT(xDataSeries.is());
    Reference<beans::XPropertySet> xDp = xDataSeries->getDataPointByIndex(1);
    Sequence<Reference<chart2::XDataPointCustomLabelField>> aLabelFields;
    CPPUNIT_ASSERT(xDp->getPropertyValue(u"CustomLabelFields"_ustr) >>= aLabelFields);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1), aLabelFields.getLength());
    CPPUNIT_ASSERT_EQUAL(u"Kiskacsa"_ustr, aLabelFields[0]->getString());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf125444PercentageCustomLabel)
{
    loadFromFile(u"pptx/tdf125444.pptx");

    // 1st chart
    Reference<chart2::XChartDocument> xChartDoc(getChartDocFromDrawImpress(0, 0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    uno::Reference<chart2::XDataSeries> xDataSeries(getDataSeriesFromDoc(xChartDoc, 0));
    CPPUNIT_ASSERT(xDataSeries.is());
    Reference<beans::XPropertySet> xDp = xDataSeries->getDataPointByIndex(1);
    Sequence<Reference<chart2::XDataPointCustomLabelField>> aLabelFields;
    CPPUNIT_ASSERT(xDp->getPropertyValue(u"CustomLabelFields"_ustr) >>= aLabelFields);
    // There are three label field: a value label, a newline and a percentage label. We want
    // to assert the latter.
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(3), aLabelFields.getLength());
    CPPUNIT_ASSERT_EQUAL(chart2::DataPointCustomLabelFieldType_PERCENTAGE,
                         aLabelFields[2]->getFieldType());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testDataPointLabelCustomPos)
{
    // test CustomLabelPosition on Bar chart
    loadFromFile(u"xlsx/testDataPointLabelCustomPos.xlsx");
    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());
    uno::Reference<chart2::XDataSeries> xDataSeries(getDataSeriesFromDoc(xChartDoc, 0));
    CPPUNIT_ASSERT(xDataSeries.is());

    uno::Reference<beans::XPropertySet> xPropertySet(xDataSeries->getDataPointByIndex(0),
                                                     uno::UNO_SET_THROW);
    CPPUNIT_ASSERT(xPropertySet.is());

    chart2::RelativePosition aCustomLabelPosition;
    xPropertySet->getPropertyValue(u"CustomLabelPosition"_ustr) >>= aCustomLabelPosition;
    CPPUNIT_ASSERT_DOUBLES_EQUAL(-0.14621409921671025, aCustomLabelPosition.Primary, 1e-7);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(-5.2887961029923464E-2, aCustomLabelPosition.Secondary, 1e-7);

    sal_Int32 aPlacement;
    xPropertySet->getPropertyValue(u"LabelPlacement"_ustr) >>= aPlacement;
    CPPUNIT_ASSERT_EQUAL(chart::DataLabelPlacement::OUTSIDE, aPlacement);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf130032)
{
    // test CustomLabelPosition on Line chart
    loadFromFile(u"xlsx/testTdf130032.xlsx");
    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());
    uno::Reference<chart2::XDataSeries> xDataSeries(getDataSeriesFromDoc(xChartDoc, 0));
    CPPUNIT_ASSERT(xDataSeries.is());

    uno::Reference<beans::XPropertySet> xPropertySet(xDataSeries->getDataPointByIndex(1),
                                                     uno::UNO_SET_THROW);
    CPPUNIT_ASSERT(xPropertySet.is());

    chart2::RelativePosition aCustomLabelPosition;
    xPropertySet->getPropertyValue(u"CustomLabelPosition"_ustr) >>= aCustomLabelPosition;
    CPPUNIT_ASSERT_DOUBLES_EQUAL(-0.0438333333333334, aCustomLabelPosition.Primary, 1e-7);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(0.086794050743657, aCustomLabelPosition.Secondary, 1e-7);

    sal_Int32 aPlacement;
    xPropertySet->getPropertyValue(u"LabelPlacement"_ustr) >>= aPlacement;
    CPPUNIT_ASSERT_EQUAL(chart::DataLabelPlacement::RIGHT, aPlacement);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf134978)
{
    // test CustomLabelPosition on Pie chart
    loadFromFile(u"xlsx/tdf134978.xlsx");
    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());
    uno::Reference<chart2::XDataSeries> xDataSeries(getDataSeriesFromDoc(xChartDoc, 0));
    CPPUNIT_ASSERT(xDataSeries.is());

    uno::Reference<beans::XPropertySet> xPropertySet(xDataSeries->getDataPointByIndex(2),
                                                     uno::UNO_SET_THROW);
    CPPUNIT_ASSERT(xPropertySet.is());

    chart2::RelativePosition aCustomLabelPosition;
    xPropertySet->getPropertyValue(u"CustomLabelPosition"_ustr) >>= aCustomLabelPosition;
    CPPUNIT_ASSERT_DOUBLES_EQUAL(-0.040273622047244093, aCustomLabelPosition.Primary, 1e-7);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(-0.25635352872557599, aCustomLabelPosition.Secondary, 1e-7);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf119138MissingAutoTitleDeleted)
{
    loadFromFile(u"xlsx/tdf119138-missing-autotitledeleted.xlsx");
    Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());

    Reference<chart2::XTitled> xTitled(xChartDoc, uno::UNO_QUERY_THROW);
    uno::Reference<chart2::XTitle> xTitle = xTitled->getTitleObject();
    CPPUNIT_ASSERT_MESSAGE(
        "Missing autoTitleDeleted is implied to be True if title text is present", xTitle.is());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testStockChartShiftedCategoryPosition)
{
    loadFromFile(u"odt/stock_chart_LO_6_2.odt");

    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xAxis.is());

    chart2::ScaleData aScaleData = xAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());
    CPPUNIT_ASSERT(aScaleData.ShiftedCategoryPosition);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf133376)
{
    // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
    // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
    if (!IsDefaultDPI())
        return;

    loadFromFile(u"xlsx/tdf133376.xlsx");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);

    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xDataPointLabel(
        getShapeByName(xShapes,
                       u"CID/MultiClick/CID/D=0:CS=0:CT=0:Series=0:DataLabels=:DataLabel=2"_ustr),
        UNO_SET_THROW);

    CPPUNIT_ASSERT(xDataPointLabel.is());
    // Check the position of the 3rd data point label, which is out from the pie slice
    awt::Point aLabelPosition = xDataPointLabel->getPosition();
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1787, aLabelPosition.X, 30);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(5569, aLabelPosition.Y, 30);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf134225)
{
    loadFromFile(u"xlsx/tdf134225.xlsx");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);

    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xDataPointLabel1(
        getShapeByName(xShapes,
                       u"CID/MultiClick/CID/D=0:CS=0:CT=0:Series=0:DataLabels=:DataLabel=0"_ustr),
        UNO_SET_THROW);
    CPPUNIT_ASSERT(xDataPointLabel1.is());

    Reference<drawing::XShape> xDataPointLabel2(
        getShapeByName(xShapes,
                       u"CID/MultiClick/CID/D=0:CS=0:CT=0:Series=0:DataLabels=:DataLabel=1"_ustr),
        UNO_SET_THROW);
    CPPUNIT_ASSERT(xDataPointLabel2.is());

#if defined(_WIN32) && !USE_HEADLESS_CODE
    // font is MS Comic Sans which we can only assume is available under windows, and only where
    // system fonts are used
    awt::Point aLabelPosition1 = xDataPointLabel1->getPosition();
    awt::Point aLabelPosition2 = xDataPointLabel2->getPosition();

    // Check the distance between the position of the 1st data point label and the second one
    CPPUNIT_ASSERT_DOUBLES_EQUAL(1493, sal_Int32(aLabelPosition2.X - aLabelPosition1.X), 30);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(2015, sal_Int32(aLabelPosition2.Y - aLabelPosition1.Y), 30);
#endif
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf136105)
{
    // FIXME: the DPI check should be removed when either (1) the test is fixed to work with
    // non-default DPI; or (2) unit tests on Windows are made to use svp VCL plugin.
    if (!IsDefaultDPI())
        return;

    loadFromFile(u"xlsx/tdf136105.xlsx");
    // 1st chart with fix inner position and size
    {
        Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);

        Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
        Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
        Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
        Reference<drawing::XShape> xDataPointLabel(
            getShapeByName(
                xShapes, u"CID/MultiClick/CID/D=0:CS=0:CT=0:Series=0:DataLabels=:DataLabel=0"_ustr),
            UNO_SET_THROW);

        CPPUNIT_ASSERT(xDataPointLabel.is());
        // Check the position of the 1st data point label, which is out from the pie slice
        awt::Point aLabelPosition = xDataPointLabel->getPosition();
        CPPUNIT_ASSERT_DOUBLES_EQUAL(8797, aLabelPosition.X, 500);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(1374, aLabelPosition.Y, 500);
    }
    // 2nd chart with auto inner position and size
    {
        Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(1), UNO_QUERY_THROW);

        Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
        Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
        Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
        Reference<drawing::XShape> xDataPointLabel(
            getShapeByName(
                xShapes, u"CID/MultiClick/CID/D=0:CS=0:CT=0:Series=0:DataLabels=:DataLabel=0"_ustr),
            UNO_SET_THROW);

        CPPUNIT_ASSERT(xDataPointLabel.is());
        // Check the position of the 1st data point label, which is out from the pie slice
        awt::Point aLabelPosition = xDataPointLabel->getPosition();
        CPPUNIT_ASSERT_DOUBLES_EQUAL(7469, aLabelPosition.X, 500);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(1550, aLabelPosition.Y, 500);
    }
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf91250)
{
    loadFromFile(u"docx/tdf91250.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());
    Reference<chart2::XInternalDataProvider> xInternalProvider(xChartDoc->getDataProvider(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xInternalProvider.is());

    Reference<chart::XComplexDescriptionAccess> xDescAccess(xInternalProvider, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xDescAccess.is());

    // Get the category labels.
    Sequence<OUString> aCategories = xDescAccess->getRowDescriptions();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aCategories.getLength());
    CPPUNIT_ASSERT_EQUAL(u"12.3254"_ustr, aCategories[0]);
    CPPUNIT_ASSERT_EQUAL(u"11.62315"_ustr, aCategories[1]);
    CPPUNIT_ASSERT_EQUAL(u"9.26"_ustr, aCategories[2]);
    CPPUNIT_ASSERT_EQUAL(u"8.657"_ustr, aCategories[3]);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf134111)
{
    // tdf134111 : To check TextBreak value is true
    loadFromFile(u"docx/tdf134111.docx");
    uno::Reference<chart::XChartDocument> xChartDoc = getChartDocFromWriter(0);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());
    uno::Reference<chart::XDiagram> mxDiagram(xChartDoc->getDiagram());
    CPPUNIT_ASSERT(mxDiagram.is());
    uno::Reference<chart::XAxisXSupplier> xAxisXSupp(mxDiagram, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xAxisXSupp.is());
    uno::Reference<beans::XPropertySet> xAxisProp(xAxisXSupp->getXAxis());
    bool bTextBreak = false;
    xAxisProp->getPropertyValue(u"TextBreak"_ustr) >>= bTextBreak;
    // Expected value of 'TextBreak' is true
    CPPUNIT_ASSERT(bTextBreak);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf136752)
{
    loadFromFile(u"xlsx/tdf136752.xlsx");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);

    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xDataPointLabel(
        getShapeByName(xShapes,
                       u"CID/MultiClick/CID/D=0:CS=0:CT=0:Series=0:DataLabels=:DataLabel=0"_ustr),
        UNO_SET_THROW);

    CPPUNIT_ASSERT(xDataPointLabel.is());
    // Check the position of the 1st data point label, which is out from the pie slice
    awt::Point aLabelPosition = xDataPointLabel->getPosition();
    CPPUNIT_ASSERT_DOUBLES_EQUAL(8675, aLabelPosition.X, 500);
    CPPUNIT_ASSERT_DOUBLES_EQUAL(952, aLabelPosition.Y, 500);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf137505)
{
    loadFromFile(u"xlsx/tdf137505.xlsx");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);

    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShape> xCustomShape(xDrawPage->getByIndex(1), UNO_QUERY_THROW);
    CPPUNIT_ASSERT(xCustomShape.is());

    float nFontSize;
    Reference<text::XText> xRange(xCustomShape, uno::UNO_QUERY_THROW);
    Reference<text::XTextCursor> xAt = xRange->createTextCursor();
    Reference<beans::XPropertySet> xProps(xAt, UNO_QUERY);
    // check the text size of custom shape, inside the chart.
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"CharHeight"_ustr) >>= nFontSize);
    CPPUNIT_ASSERT_EQUAL(float(12), nFontSize);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf137734)
{
    loadFromFile(u"xlsx/tdf137734.xlsx");
    Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());
    CPPUNIT_ASSERT(xChartDoc.is());
    Reference<chart2::XDataSeries> xDataSeries = getDataSeriesFromDoc(xChartDoc, 0);
    CPPUNIT_ASSERT(xDataSeries.is());
    Reference<beans::XPropertySet> xPropSet(xDataSeries, uno::UNO_QUERY_THROW);
    cpo::uno::Any aAny = xPropSet->getPropertyValue(u"VaryColorsByPoint"_ustr);
    bool bVaryColor = true;
    CPPUNIT_ASSERT(aAny >>= bVaryColor);
    CPPUNIT_ASSERT(!bVaryColor);

    // tdf#126133 Test primary X axis Rotation value
    Reference<chart2::XAxis> xXAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xXAxis.is());
    Reference<chart2::XTitled> xTitled(xXAxis, uno::UNO_QUERY_THROW);
    Reference<chart2::XTitle> xTitle = xTitled->getTitleObject();
    CPPUNIT_ASSERT(xTitle.is());
    Reference<beans::XPropertySet> xTitlePropSet(xTitle, uno::UNO_QUERY_THROW);
    cpo::uno::Any aAny2 = xTitlePropSet->getPropertyValue(u"TextRotation"_ustr);
    double nRotation = -1;
    CPPUNIT_ASSERT(aAny2 >>= nRotation);
    CPPUNIT_ASSERT_EQUAL(0.0, nRotation);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf137874)
{
    loadFromFile(u"xlsx/piechart_legend.xlsx");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);
    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xLegendEntry;
    xLegendEntry = getShapeByName(
        xShapes, u"CID/MultiClick/D=0:CS=0:CT=0:Series=0:Point=0:LegendEntry=0"_ustr);
    CPPUNIT_ASSERT(xLegendEntry.is());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf146463)
{
    loadFromFile(u"ods/tdf146463.ods");
    Reference<chart2::XChartDocument> xChartDoc(getChartDocFromSheet(0));
    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0);
    std::vector<std::vector<double>> aDataSeriesYValues
        = getDataSeriesYValuesFromChartType(xChartType);
    size_t nLegendEntryCount = aDataSeriesYValues.size();
    CPPUNIT_ASSERT_EQUAL(size_t(14), nLegendEntryCount);

    for (size_t nSeriesIndex = 0; nSeriesIndex < nLegendEntryCount; ++nSeriesIndex)
    {
        uno::Reference<drawing::XShape> xLegendEntry
            = getShapeByName(xShapes, "CID/MultiClick/D=0:CS=0:CT=0:Series="
                                          + OUString::number(nSeriesIndex) + ":LegendEntry=0");
        if (nSeriesIndex == 0)
            CPPUNIT_ASSERT_MESSAGE("Legend 0 is not visible", xLegendEntry.is());
        else
            CPPUNIT_ASSERT_MESSAGE(
                OString("Legend " + OString::number(nSeriesIndex) + " is visible").getStr(),
                !xLegendEntry.is());
    }
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdfCustomShapePos)
{
    loadFromFile(u"docx/testcustomshapepos.docx");
    Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), UNO_QUERY_THROW);
    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    // test position and size of a custom shape within a chart, rotated by 0 degree.
    {
        Reference<drawing::XShape> xCustomShape(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
        awt::Point aPosition = xCustomShape->getPosition();
        CPPUNIT_ASSERT_DOUBLES_EQUAL(8845, aPosition.X, 300);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(855, aPosition.Y, 300);
        awt::Size aSize = xCustomShape->getSize();
        CPPUNIT_ASSERT_DOUBLES_EQUAL(4831, aSize.Width, 300);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(1550, aSize.Height, 300);
    }
    // test position and size of a custom shape within a chart, rotated by 90 degree.
    {
        Reference<drawing::XShape> xCustomShape(xDrawPage->getByIndex(1), UNO_QUERY_THROW);
        awt::Point aPosition = xCustomShape->getPosition();
        CPPUNIT_ASSERT_DOUBLES_EQUAL(1658, aPosition.X, 300);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(6119, aPosition.Y, 300);
        awt::Size aSize = xCustomShape->getSize();
        CPPUNIT_ASSERT_DOUBLES_EQUAL(4165, aSize.Width, 300);
        CPPUNIT_ASSERT_DOUBLES_EQUAL(1334, aSize.Height, 300);
    }
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf121281)
{
    loadFromFile(u"xlsx/incorrect_label_position.xlsx");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);
    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xDataPointLabel(
        getShapeByName(xShapes,
                       u"CID/MultiClick/CID/D=0:CS=0:CT=0:Series=0:DataLabels=:DataLabel=0"_ustr),
        UNO_SET_THROW);

    CPPUNIT_ASSERT(xDataPointLabel.is());
    awt::Point aLabelPosition = xDataPointLabel->getPosition();
    // This failed, if the data label flowed out of the chart area.
    CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), aLabelPosition.Y);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf139658)
{
    loadFromFile(u"docx/tdf139658.docx");
    uno::Reference<chart2::XChartDocument> xChartDoc(getChartDocFromWriter(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());
    Reference<chart2::XInternalDataProvider> xInternalProvider(xChartDoc->getDataProvider(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xInternalProvider.is());

    Reference<chart::XComplexDescriptionAccess> xDescAccess(xInternalProvider, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xDescAccess.is());

    // Get the category labels.
    Sequence<OUString> aCategories = xDescAccess->getRowDescriptions();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aCategories.getLength());
    CPPUNIT_ASSERT_EQUAL(u"category1"_ustr, aCategories[0]);
    CPPUNIT_ASSERT_EQUAL(u"\"category2\""_ustr, aCategories[1]);
    CPPUNIT_ASSERT_EQUAL(u"category\"3"_ustr, aCategories[2]);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf146066)
{
    loadFromFile(u"ods/tdf146066.ods");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);
    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, uno::UNO_QUERY);
    uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
    uno::Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xShapes.is());

    uno::Reference<drawing::XShape> xYAxisShape = getShapeByName(
        xShapes, u"CID/D=0:CS=0:Axis=1,0"_ustr, // Y Axis
        // Axis occurs twice in chart xshape representation so need to get the one related to labels
        [](const uno::Reference<drawing::XShape>& rXShape) -> bool {
            uno::Reference<drawing::XShapes> xAxisShapes(rXShape, uno::UNO_QUERY);
            CPPUNIT_ASSERT(xAxisShapes.is());
            uno::Reference<drawing::XShape> xChildShape(xAxisShapes->getByIndex(0), uno::UNO_QUERY);
            uno::Reference<drawing::XShapeDescriptor> xShapeDescriptor(xChildShape,
                                                                       uno::UNO_QUERY_THROW);
            return (xShapeDescriptor->getShapeType() == "com.sun.star.drawing.TextShape");
        });
    CPPUNIT_ASSERT(xYAxisShape.is());

    // Check label count
    uno::Reference<container::XIndexAccess> xIndexAccess(xYAxisShape, UNO_QUERY_THROW);
    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(8), xIndexAccess->getCount());

    // Check text
    uno::Reference<text::XTextRange> xLabel0(xIndexAccess->getByIndex(0), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"0"_ustr, xLabel0->getString());
    uno::Reference<text::XTextRange> xLabel1(xIndexAccess->getByIndex(1), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"5"_ustr, xLabel1->getString());
    uno::Reference<text::XTextRange> xLabel2(xIndexAccess->getByIndex(2), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"10"_ustr, xLabel2->getString());
    uno::Reference<text::XTextRange> xLabel3(xIndexAccess->getByIndex(3), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"15"_ustr, xLabel3->getString());
    uno::Reference<text::XTextRange> xLabel4(xIndexAccess->getByIndex(4), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"20"_ustr, xLabel4->getString());
    uno::Reference<text::XTextRange> xLabel5(xIndexAccess->getByIndex(5), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"25"_ustr, xLabel5->getString());
    uno::Reference<text::XTextRange> xLabel6(xIndexAccess->getByIndex(6), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"30"_ustr, xLabel6->getString());
    uno::Reference<text::XTextRange> xLabel7(xIndexAccess->getByIndex(7), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(u"35"_ustr, xLabel7->getString());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf150434)
{
    loadFromFile(u"xlsx/tdf150434.xlsx");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);
    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xLegend = getShapeByName(xShapes, u"CID/D=0:Legend="_ustr);
    CPPUNIT_ASSERT(xLegend.is());
    awt::Point aPosition = xLegend->getPosition();

    // This failed, if the legend flowed out of the chart area.
    CPPUNIT_ASSERT_GREATEREQUAL(static_cast<sal_Int32>(0), aPosition.Y);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testChartDataTableWithMultipleLegendEntriesForOneDataSeries)
{
    loadFromFile(u"xlsx/DataTable-MultipleLegendEntriesForOneDataSeries.xlsx");
    // Loading this file caused a crash in the data table code

    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);
    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);
    Reference<drawing::XShape> xDataTableShape
        = getShapeByName(xShapes, u"CID/D=0:DataTable="_ustr);
    CPPUNIT_ASSERT(xDataTableShape.is());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf159450AxisTitleWithDataTable)
{
    // The chart has a vertical axis title and a data table below the plot area.
    // The axis title must stay centered on the plot area, not on the whole
    // diagram including the data table below it (which used to shift it down).
    loadFromFile(u"xlsx/tdf159450.xlsx");
    auto xDrawPage
        = getChartDocFromSheet(0).queryThrow<drawing::XDrawPageSupplier>()->getDrawPage();
    auto xShapes = xDrawPage->getByIndex(0).queryThrow<drawing::XShapes>();

    // The chart has a data table, drawn below the plot area.
    CPPUNIT_ASSERT(getShapeByName(xShapes, u"CID/D=0:DataTable="_ustr).is());
    Reference<drawing::XShape> xPlotArea = getShapeByName(xShapes, u"PlotAreaExcludingAxes"_ustr);
    CPPUNIT_ASSERT(xPlotArea.is());
    Reference<drawing::XShape> xAxisTitle
        = getShapeByName(xShapes, u"CID/D=0:CS=0:Axis=1,0:Title="_ustr);
    CPPUNIT_ASSERT(xAxisTitle.is());

    // The vertical axis title is centered on the plot area. Before the fix it was
    // centered on the plot area plus the data table below it, so its center sat
    // about half the data table height lower.
    sal_Int32 nTitleCenterY = xAxisTitle->getPosition().Y + xAxisTitle->getSize().Height / 2;
    sal_Int32 nPlotCenterY = xPlotArea->getPosition().Y + xPlotArea->getSize().Height / 2;
    CPPUNIT_ASSERT_DOUBLES_EQUAL(nPlotCenterY, nTitleCenterY, 20);
}

namespace
{
void lcl_assertAngles(const Reference<chart2::XAxis>& rAxis, const double& rExpectedLabelAngle,
                      const double& rExpectedTitleAngle)
{
    Reference<beans::XPropertySet> xPS(rAxis, uno::UNO_QUERY_THROW);
    double fAxisLabelAngle = 0.0;
    xPS->getPropertyValue(u"TextRotation"_ustr) >>= fAxisLabelAngle;
    CPPUNIT_ASSERT_DOUBLES_EQUAL(rExpectedLabelAngle, fAxisLabelAngle, 1e-10);

    Reference<chart2::XTitled> xAxisTitled(rAxis, uno::UNO_QUERY_THROW);
    Reference<chart2::XTitle> xAxisTitle = xAxisTitled->getTitleObject();
    CPPUNIT_ASSERT(xAxisTitle.is());
    Reference<beans::XPropertySet> xPropSet(xAxisTitle, uno::UNO_QUERY_THROW);
    double fAxisTitleAngle = 0.0;
    xPropSet->getPropertyValue(u"TextRotation"_ustr) >>= fAxisTitleAngle;
    CPPUNIT_ASSERT_DOUBLES_EQUAL(rExpectedTitleAngle, fAxisTitleAngle, 1e-10);
}
} // end namespace

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testAngleUnits)
{
    loadFromFile(u"fods/tdf161483_AngleUnits.fods");
    double fExpXAxisLabelAngle = 344.61; // = 382.9grad = 6.01457913529766rad
    double fExpXAxisTitleAngle = 342.63; // = 380.7grad = 5.98002161610817rad
    double fExpYAxisLabelAngle = 15.12; // = 16.8grad = 0.263893782901543rad
    double fExpYAxisTitleAngle = 14.94; // = 16.6grad = 0.260752190247953rad
    double fExpZAxisLabelAngle = 344.16; // = 382.4grad = 6.00672515366369rad
    double fExpZAxisTitleAngle = 60.39; // = 67.1grad = 1.05400433527938rad

    // sheet 0: no unit; sheet 1: unit deg; sheet 2: unit rad; sheet 3: unit grad
    // Without fix, the values with unit grad and rad were read as if they are in degrees.
    for (size_t i = 0; i < 4; i++)
    {
        uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(i);
        CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());

        // x-axis
        Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
        CPPUNIT_ASSERT(xAxis.is());
        lcl_assertAngles(xAxis, fExpXAxisLabelAngle, fExpXAxisTitleAngle);

        // y-axis
        xAxis = getAxisFromDoc(xChartDoc, 0, 1, 0);
        CPPUNIT_ASSERT(xAxis.is());
        lcl_assertAngles(xAxis, fExpYAxisLabelAngle, fExpYAxisTitleAngle);

        //z-axis
        xAxis = getAxisFromDoc(xChartDoc, 0, 2, 0);
        CPPUNIT_ASSERT(xAxis.is());
        lcl_assertAngles(xAxis, fExpZAxisLabelAngle, fExpZAxisTitleAngle);
    }
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf166428)
{
    // The file was generated in ODF 1.3 by LO 24.8
    loadFromFile(u"ods/tdf166428_Low_High_StockChart_LO248.ods");

    // The attribute table:cell-range-address does not exist in ODF 1.4. and therefore may not be
    // be written.
    saveAndReload(TestFilter::ODS);
    xmlDocUniquePtr pXmlDoc = parseExport(u"Object 1/content.xml"_ustr);
    assertXPathNoAttribute(pXmlDoc,
                           "/office:document-content/office:body/office:chart/chart:chart/"
                           "chart:plot-area",
                           "table:cell-range-address");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    // Without applied fix, the series were not merged to one stock chart series and therefore
    // only role "values-y" existed.
    Reference<chart2::data::XDataSequence> xDataSeq
        = getDataSequenceFromDocByRole(xChartDoc, u"values-min");
    CPPUNIT_ASSERT(xDataSeq.is());
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramODSRoundtrip)
{
    // See tdf#163727
    loadFromFile(u"fods/tdf163727_histogram_roundtrip.fods");

    // Round trip 1: chart type identity, raw data unchanged
    saveAndReload(TestFilter::ODS);

    xmlDocUniquePtr pXmlDoc = parseExport(u"Object 1/content.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart"
                         "[@chart:class='chart:bar']");
    assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart"
                         "[@coext:class='coext:histogram']");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());
    CPPUNIT_ASSERT_EQUAL(u"com.sun.star.chart2.HistogramChartType"_ustr,
                         xChartType->getChartType());

    // Raw values-y preserved, calculated-y not persisted.
    Reference<chart2::data::XDataSequence> xValuesY
        = getDataSequenceFromDocByRole(xChartDoc, u"values-y");
    CPPUNIT_ASSERT(xValuesY.is());

    Reference<chart2::data::XDataSequence> xCalculatedY
        = getDataSequenceFromDocByRole(xChartDoc, u"calculated-y");
    CPPUNIT_ASSERT(!xCalculatedY.is());

    // After reload the series must carry a categories sequence with bin range
    // labels, not just the raw values-y.
    Reference<chart2::data::XDataSequence> xCategories
        = getDataSequenceFromDocByRole(xChartDoc, u"categories");
    CPPUNIT_ASSERT(xCategories.is());
    Reference<chart2::data::XTextualDataSequence> xCategoriesText(xCategories, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xCategoriesText.is());
    const Sequence<OUString> aBinLabels = xCategoriesText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"[10-13.94]"_ustr, aBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(13.94-17.87]"_ustr, aBinLabels[1]);

    // Round trip 2: non-default binning parameters survive
    Reference<beans::XPropertySet> xProps(xChartType, uno::UNO_QUERY_THROW);
    xProps->setPropertyValue(u"FrequencyType"_ustr, cpo::uno::Any(sal_Int32(1)));
    xProps->setPropertyValue(u"BinWidth"_ustr, cpo::uno::Any(3.5));

    saveAndReload(TestFilter::ODS);

    pXmlDoc = parseExport(u"Object 1/content.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    static constexpr char sHistogramProps[]
        = "/office:document-content/office:automatic-styles/style:style[@style:family='chart']/"
          "style:chart-properties[@coext:frequency-type]";
    assertXPath(pXmlDoc, sHistogramProps, "frequency-type", u"1");
    assertXPath(pXmlDoc, sHistogramProps, "bin-width", u"3.5");

    xChartDoc = getChartDocFromSheet(0);
    xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    Reference<beans::XPropertySet> xReloadedProps(xChartType, uno::UNO_QUERY_THROW);

    sal_Int32 nFrequencyType = -1;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), nFrequencyType);

    double fBinWidth = 0.0;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"BinWidth"_ustr) >>= fBinWidth);
    CPPUNIT_ASSERT_EQUAL(3.5, fBinWidth);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testCorrelationCircleEqualScales)
{
    loadFromFile(u"fods/correlation-circle.fods");
    Reference<chart::XChartDocument> xChartDoc(getChartDocFromSheet(0), UNO_QUERY_THROW);

    Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xChartDoc, UNO_QUERY_THROW);
    Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage(), UNO_SET_THROW);
    Reference<drawing::XShapes> xShapes(xDrawPage->getByIndex(0), UNO_QUERY_THROW);

    // Both dimensions run from minus one to one, so the plot area has to be
    // square for the two of them to be at the same scale, which is what makes
    // the circle of radius one a circle rather than an ellipse.
    Reference<drawing::XShape> xPlotArea(
        getShapeByName(xShapes, u"PlotAreaExcludingAxes"_ustr), UNO_SET_THROW);
    const awt::Size aPlotArea = xPlotArea->getSize();
    CPPUNIT_ASSERT_GREATER(sal_Int32(0), aPlotArea.Width);
    CPPUNIT_ASSERT_EQUAL(aPlotArea.Height, aPlotArea.Width);

    // The same thing said through the axes: they are drawn the same length.
    Reference<drawing::XShape> xAcross(
        getShapeByName(xShapes, u"CID/D=0:CS=0:Axis=0,0"_ustr), UNO_SET_THROW);
    Reference<drawing::XShape> xUp(
        getShapeByName(xShapes, u"CID/D=0:CS=0:Axis=1,0"_ustr), UNO_SET_THROW);
    CPPUNIT_ASSERT_EQUAL(xUp->getSize().Height, xAcross->getSize().Width);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testCorrelationCircleODSRoundtrip)
{
    loadFromFile(u"fods/correlation-circle.fods");

    saveAndReload(TestFilter::ODS);

    xmlDocUniquePtr pXmlDoc = parseExport(u"Object 1/content.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    // A reader that knows nothing of this chart type is left with points in a
    // plane, and the name of the type itself rides in the coext namespace.
    assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart"
                         "[@chart:class='chart:scatter']");
    assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart"
                         "[@coext:class='coext:correlation-circle']");

    // Each series keeps its feature column as the series range and the two
    // dimension columns as a domain each.
    assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart"
                         "/chart:plot-area/chart:series", 3);
    assertXPath(pXmlDoc, "/office:document-content/office:body/office:chart/chart:chart"
                         "/chart:plot-area/chart:series[1]/chart:domain", 2);

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());
    CPPUNIT_ASSERT_EQUAL(u"com.sun.star.chart2.CorrelationCircleChartType"_ustr,
                         xChartType->getChartType());

    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), getNumberOfDataSeries(xChartDoc));

    // One series per feature column, every one of them against the same two
    // dimension columns.
    const OUString aFeatureRanges[]
        = { u"$Sheet1.$A$2:$A$6"_ustr, u"$Sheet1.$B$2:$B$6"_ustr, u"$Sheet1.$C$2:$C$6"_ustr };
    for (sal_Int32 nSeries = 0; nSeries < 3; ++nSeries)
    {
        Reference<chart2::data::XDataSequence> xFeature
            = getDataSequenceFromDocByRole(xChartDoc, u"values-feature", nSeries);
        CPPUNIT_ASSERT(xFeature.is());
        CPPUNIT_ASSERT_EQUAL(aFeatureRanges[nSeries], xFeature->getSourceRangeRepresentation());

        Reference<chart2::data::XDataSequence> xFirstDimension
            = getDataSequenceFromDocByRole(xChartDoc, u"values-x", nSeries);
        CPPUNIT_ASSERT(xFirstDimension.is());
        CPPUNIT_ASSERT_EQUAL(u"$Sheet1.$D$2:$D$6"_ustr,
                             xFirstDimension->getSourceRangeRepresentation());

        Reference<chart2::data::XDataSequence> xSecondDimension
            = getDataSequenceFromDocByRole(xChartDoc, u"values-y", nSeries);
        CPPUNIT_ASSERT(xSecondDimension.is());
        CPPUNIT_ASSERT_EQUAL(u"$Sheet1.$E$2:$E$6"_ustr,
                             xSecondDimension->getSourceRangeRepresentation());
    }
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramXLSXRoundtrip)
{
    // See tdf#163727
    // OOXML chartex represents histograms as <cx:series layoutId="clusteredColumn">
    // with a <cx:layoutPr><cx:binning/></cx:layoutPr> child.
    // On import that should be detected as a HistogramChartType and on export the binning
    // markup should stay preserved.

    loadFromFile(u"xlsx/SimpleHistogram.xlsx");

    // Round trip 1: chart type identity + binning element preserved.
    saveAndReload(TestFilter::XLSX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    assertXPath(pXmlDoc, "/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series",
                "layoutId", u"clusteredColumn");
    assertXPath(pXmlDoc,
                "/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series/cx:layoutPr/"
                "cx:binning",
                "intervalClosed", u"r");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());
    CPPUNIT_ASSERT_EQUAL(u"com.sun.star.chart2.HistogramChartType"_ustr,
                         xChartType->getChartType());

    // Raw values-y must remain the source data; calculated-y is regenerated.
    Reference<chart2::data::XDataSequence> xValuesY
        = getDataSequenceFromDocByRole(xChartDoc, u"values-y");
    CPPUNIT_ASSERT(xValuesY.is());

    Reference<chart2::data::XDataSequence> xCalculatedY
        = getDataSequenceFromDocByRole(xChartDoc, u"calculated-y");
    CPPUNIT_ASSERT(!xCalculatedY.is());

    // The X axis must carry the bin range labels from the histogram template,
    // not the generic "1", "2", ... labels that the OOXML axis converter
    // produces by default.
    Reference<chart2::XAxis> xXAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xXAxis.is());
    chart2::ScaleData aScaleData = xXAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());
    Reference<chart2::data::XTextualDataSequence> xAxisCatText(aScaleData.Categories->getValues(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xAxisCatText.is());
    const Sequence<OUString> aAxisBinLabels = xAxisCatText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aAxisBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"[10-13.94]"_ustr, aAxisBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(13.94-17.88]"_ustr, aAxisBinLabels[1]);

    // Round trip 2: non-default binning parameters survive save + reload.
    Reference<beans::XPropertySet> xProperties(xChartType, uno::UNO_QUERY_THROW);
    xProperties->setPropertyValue(u"FrequencyType"_ustr, cpo::uno::Any(sal_Int32(1)));
    xProperties->setPropertyValue(u"BinWidth"_ustr, cpo::uno::Any(2.5));

    saveAndReload(TestFilter::XLSX);

    pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);
    assertXPath(pXmlDoc,
                "/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series/cx:layoutPr/"
                "cx:binning/cx:binSize",
                "val", u"2.5");

    xChartDoc = getChartDocFromSheet(0);
    xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    Reference<beans::XPropertySet> xReloadedProperties(xChartType, uno::UNO_QUERY_THROW);

    sal_Int32 nFrequencyType = -1;
    CPPUNIT_ASSERT(xReloadedProperties->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), nFrequencyType);

    double fBinWidth = 0.0;
    CPPUNIT_ASSERT(xReloadedProperties->getPropertyValue(u"BinWidth"_ustr) >>= fBinWidth);
    CPPUNIT_ASSERT_EQUAL(2.5, fBinWidth);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramBinCountRoundtrip_ODS)
{
    // ODF: fixed number of bins survives export/reload at the model level.
    loadFromFile(u"fods/tdf163727_histogram_roundtrip.fods");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xProps(xChartType, uno::UNO_QUERY_THROW);
    xProps->setPropertyValue(u"FrequencyType"_ustr, cpo::uno::Any(sal_Int32(2)));
    xProps->setPropertyValue(u"BinCount"_ustr, cpo::uno::Any(sal_Int32(3)));

    saveAndReload(TestFilter::ODS);

    xmlDocUniquePtr pXmlDoc = parseExport(u"Object 1/content.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    static constexpr char sHistogramProps[]
        = "/office:document-content/office:automatic-styles/style:style[@style:family='chart']/"
          "style:chart-properties[@coext:frequency-type]";
    assertXPath(pXmlDoc, sHistogramProps, "frequency-type", u"2");
    assertXPath(pXmlDoc, sHistogramProps, "bin-count", u"3");

    xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xReloadedProps(xChartType, uno::UNO_QUERY_THROW);

    sal_Int32 nFrequencyType = -1;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), nFrequencyType);

    sal_Int32 nBinCount = -1;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"BinCount"_ustr) >>= nBinCount);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), nBinCount);

    Reference<chart2::data::XDataSequence> xCategories
        = getDataSequenceFromDocByRole(xChartDoc, u"categories");
    CPPUNIT_ASSERT(xCategories.is());

    Reference<chart2::data::XTextualDataSequence> xCategoriesText(xCategories, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xCategoriesText.is());

    const Sequence<OUString> aBinLabels = xCategoriesText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"[10-11.67]"_ustr, aBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(11.67-13.33]"_ustr, aBinLabels[1]);
    CPPUNIT_ASSERT_EQUAL(u"(13.33-15]"_ustr, aBinLabels[2]);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramBinCountRoundtrip_XLSX)
{
    // XLSX: fixed number of bins is written as cx:binCount and survives reload.
    loadFromFile(u"xlsx/SimpleHistogram.xlsx");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xXlsxProps(xChartType, uno::UNO_QUERY_THROW);
    xXlsxProps->setPropertyValue(u"FrequencyType"_ustr, cpo::uno::Any(sal_Int32(2)));
    xXlsxProps->setPropertyValue(u"BinCount"_ustr, cpo::uno::Any(sal_Int32(3)));

    saveAndReload(TestFilter::XLSX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    assertXPath(pXmlDoc,
                "/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series/cx:layoutPr/"
                "cx:binning/cx:binCount",
                "val", u"3");

    xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<chart2::XAxis> xXAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xXAxis.is());

    chart2::ScaleData aScaleData = xXAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());

    Reference<chart2::data::XTextualDataSequence> xAxisCatText(aScaleData.Categories->getValues(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xAxisCatText.is());

    const Sequence<OUString> aAxisBinLabels = xAxisCatText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aAxisBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"[10-11.67]"_ustr, aAxisBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(11.67-13.33]"_ustr, aAxisBinLabels[1]);
    CPPUNIT_ASSERT_EQUAL(u"(13.33-15]"_ustr, aAxisBinLabels[2]);

    Reference<beans::XPropertySet> xReloadedXlsxProps(xChartType, uno::UNO_QUERY_THROW);

    sal_Int32 nFrequencyType = -1;
    CPPUNIT_ASSERT(xReloadedXlsxProps->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(2), nFrequencyType);

    sal_Int32 nBinCount = -1;
    CPPUNIT_ASSERT(xReloadedXlsxProps->getPropertyValue(u"BinCount"_ustr) >>= nBinCount);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(3), nBinCount);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramUnderflowOverflowBins)
{
    loadFromFile(u"fods/tdf163727_histogram_roundtrip.fods");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xProps(xChartType, uno::UNO_QUERY_THROW);
    xProps->setPropertyValue(u"FrequencyType"_ustr, cpo::uno::Any(sal_Int32(1)));
    xProps->setPropertyValue(u"BinWidth"_ustr, cpo::uno::Any(2.0));
    xProps->setPropertyValue(u"UseUnderflowBin"_ustr, cpo::uno::Any(true));
    xProps->setPropertyValue(u"UnderflowBinValue"_ustr, cpo::uno::Any(11.0));
    xProps->setPropertyValue(u"UseOverflowBin"_ustr, cpo::uno::Any(true));
    xProps->setPropertyValue(u"OverflowBinValue"_ustr, cpo::uno::Any(14.0));

    Reference<chart2::data::XDataSequence> xCategories
        = getDataSequenceFromDocByRole(xChartDoc, u"categories");
    CPPUNIT_ASSERT(xCategories.is());

    Reference<chart2::data::XTextualDataSequence> xCategoriesText(xCategories, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xCategoriesText.is());

    const Sequence<OUString> aBinLabels = xCategoriesText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"<= 11"_ustr, aBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(11-13]"_ustr, aBinLabels[1]);
    CPPUNIT_ASSERT_EQUAL(u"(13-14]"_ustr, aBinLabels[2]);
    CPPUNIT_ASSERT_EQUAL(u"> 14"_ustr, aBinLabels[3]);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramODSToXLSXExport)
{
    // Exporting an ODF-origin histogram to XLSX must write the raw
    // cell range, not an _xlchart.v1.N alias with no matching definedName.
    loadFromFile(u"fods/tdf163727_histogram_roundtrip.fods");
    saveAndReload(TestFilter::XLSX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    assertXPath(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim", "type", u"val");

    OUString aFormula
        = getXPathContent(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim/cx:f");
    OString aMessage = "got formula: " + OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8);
    CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(), !aFormula.startsWith("_xlchart"));
    // The FODS series points at Sheet1.A1:A5. After conversion to XLSX
    // notation the range should look like Sheet1!$A$1:$A$5.
    CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(), aFormula.indexOf(u"Sheet1") >= 0);
    CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(), aFormula.indexOf(u"$A$1") >= 0);
    CPPUNIT_ASSERT_MESSAGE(aMessage.getStr(), aFormula.indexOf(u"$A$5") >= 0);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramMultiChartODSToXLSXExport)
{
    // Multi-chart export must not reuse the same ChartEx formula for all histograms.
    loadFromFile(u"fods/tdf163727_histogram_roundtrip_multi_chart.fods");
    saveAndReload(TestFilter::XLSX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);
    CPPUNIT_ASSERT_EQUAL(
        u"Sheet1!$A$1:$A$6"_ustr,
        getXPathContent(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim/cx:f"));

    pXmlDoc = parseExport(u"xl/charts/chartEx2.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);
    CPPUNIT_ASSERT_EQUAL(
        u"Sheet1!$B$1:$B$5"_ustr,
        getXPathContent(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim/cx:f"));

    pXmlDoc = parseExport(u"xl/charts/chartEx3.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);
    CPPUNIT_ASSERT_EQUAL(
        u"Sheet1!$C$1:$C$6"_ustr,
        getXPathContent(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim/cx:f"));
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramMultiChartXLSXRoundtrip)
{
    // Native ChartEx roundtrip must keep each histogram bound to its own source range.
    loadFromFile(u"xlsx/tdf163727_histogram_roundtrip_multi_chart.xlsx");
    saveAndReload(TestFilter::XLSX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);
    CPPUNIT_ASSERT_EQUAL(
        u"Sheet1!$A$1:$A$6"_ustr,
        getXPathContent(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim/cx:f"));

    pXmlDoc = parseExport(u"xl/charts/chartEx2.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);
    CPPUNIT_ASSERT_EQUAL(
        u"Sheet1!$B$1:$B$5"_ustr,
        getXPathContent(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim/cx:f"));

    pXmlDoc = parseExport(u"xl/charts/chartEx3.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);
    CPPUNIT_ASSERT_EQUAL(
        u"Sheet1!$C$1:$C$6"_ustr,
        getXPathContent(pXmlDoc, "/cx:chartSpace/cx:chartData/cx:data/cx:numDim/cx:f"));
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramODFUnderflowOverflowRoundtrip)
{
    loadFromFile(u"fods/tdf163727_histogram_roundtrip.fods");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xProps(xChartType, uno::UNO_QUERY_THROW);
    xProps->setPropertyValue(u"FrequencyType"_ustr, cpo::uno::Any(sal_Int32(1)));
    xProps->setPropertyValue(u"BinWidth"_ustr, cpo::uno::Any(2.0));
    xProps->setPropertyValue(u"UseUnderflowBin"_ustr, cpo::uno::Any(true));
    xProps->setPropertyValue(u"UnderflowBinValue"_ustr, cpo::uno::Any(11.0));
    xProps->setPropertyValue(u"UseOverflowBin"_ustr, cpo::uno::Any(true));
    xProps->setPropertyValue(u"OverflowBinValue"_ustr, cpo::uno::Any(14.0));

    saveAndReload(TestFilter::ODS);

    xmlDocUniquePtr pXmlDoc = parseExport(u"Object 1/content.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    static constexpr char sHistogramProps[]
        = "/office:document-content/office:automatic-styles/style:style[@style:family='chart']/"
          "style:chart-properties[@coext:frequency-type]";

    assertXPath(pXmlDoc, sHistogramProps, "frequency-type", u"1");
    assertXPath(pXmlDoc, sHistogramProps, "bin-width", u"2");
    assertXPath(pXmlDoc, sHistogramProps, "use-underflow-bin", u"true");
    assertXPath(pXmlDoc, sHistogramProps, "underflow-bin-value", u"11");
    assertXPath(pXmlDoc, sHistogramProps, "use-overflow-bin", u"true");
    assertXPath(pXmlDoc, sHistogramProps, "overflow-bin-value", u"14");

    xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<chart2::data::XDataSequence> xCategories
        = getDataSequenceFromDocByRole(xChartDoc, u"categories");
    CPPUNIT_ASSERT(xCategories.is());

    Reference<chart2::data::XTextualDataSequence> xCategoriesText(xCategories, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xCategoriesText.is());

    const Sequence<OUString> aBinLabels = xCategoriesText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"<= 11"_ustr, aBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(11-13]"_ustr, aBinLabels[1]);
    CPPUNIT_ASSERT_EQUAL(u"(13-14]"_ustr, aBinLabels[2]);
    CPPUNIT_ASSERT_EQUAL(u"> 14"_ustr, aBinLabels[3]);

    Reference<beans::XPropertySet> xReloadedProps(xChartType, uno::UNO_QUERY_THROW);

    bool bUseUnderflowBin = false;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"UseUnderflowBin"_ustr) >>= bUseUnderflowBin);
    CPPUNIT_ASSERT(bUseUnderflowBin);

    double fUnderflowBinValue = 0.0;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"UnderflowBinValue"_ustr)
                   >>= fUnderflowBinValue);
    CPPUNIT_ASSERT_EQUAL(11.0, fUnderflowBinValue);

    bool bUseOverflowBin = false;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"UseOverflowBin"_ustr) >>= bUseOverflowBin);
    CPPUNIT_ASSERT(bUseOverflowBin);

    double fOverflowBinValue = 0.0;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"OverflowBinValue"_ustr)
                   >>= fOverflowBinValue);
    CPPUNIT_ASSERT_EQUAL(14.0, fOverflowBinValue);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramODFUnderflowOverflowImport)
{
    loadFromFile(u"fods/tdf163727_histogram_underflow_overflow.fods");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xProps(xChartType, uno::UNO_QUERY_THROW);

    sal_Int32 nFrequencyType = -1;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), nFrequencyType);

    double fBinWidth = 0.0;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"BinWidth"_ustr) >>= fBinWidth);
    CPPUNIT_ASSERT_EQUAL(2.0, fBinWidth);

    bool bUseUnderflowBin = false;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"UseUnderflowBin"_ustr) >>= bUseUnderflowBin);
    CPPUNIT_ASSERT(bUseUnderflowBin);

    double fUnderflowBinValue = 0.0;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"UnderflowBinValue"_ustr) >>= fUnderflowBinValue);
    CPPUNIT_ASSERT_EQUAL(11.0, fUnderflowBinValue);

    bool bUseOverflowBin = false;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"UseOverflowBin"_ustr) >>= bUseOverflowBin);
    CPPUNIT_ASSERT(bUseOverflowBin);

    double fOverflowBinValue = 0.0;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"OverflowBinValue"_ustr) >>= fOverflowBinValue);
    CPPUNIT_ASSERT_EQUAL(14.0, fOverflowBinValue);

    Reference<chart2::data::XDataSequence> xCategories
        = getDataSequenceFromDocByRole(xChartDoc, u"categories");
    CPPUNIT_ASSERT(xCategories.is());

    Reference<chart2::data::XTextualDataSequence> xCategoriesText(xCategories, uno::UNO_QUERY);
    CPPUNIT_ASSERT(xCategoriesText.is());

    const Sequence<OUString> aBinLabels = xCategoriesText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"<= 11"_ustr, aBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(11-13]"_ustr, aBinLabels[1]);
    CPPUNIT_ASSERT_EQUAL(u"(13-14]"_ustr, aBinLabels[2]);
    CPPUNIT_ASSERT_EQUAL(u"> 14"_ustr, aBinLabels[3]);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramOOXMLUnderflowOverflowRoundtrip)
{
    loadFromFile(u"fods/tdf163727_histogram_roundtrip.fods");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xProps(xChartType, uno::UNO_QUERY_THROW);
    xProps->setPropertyValue(u"FrequencyType"_ustr, cpo::uno::Any(sal_Int32(1)));
    xProps->setPropertyValue(u"BinWidth"_ustr, cpo::uno::Any(2.0));
    xProps->setPropertyValue(u"UseUnderflowBin"_ustr, cpo::uno::Any(true));
    xProps->setPropertyValue(u"UnderflowBinValue"_ustr, cpo::uno::Any(11.0));
    xProps->setPropertyValue(u"UseOverflowBin"_ustr, cpo::uno::Any(true));
    xProps->setPropertyValue(u"OverflowBinValue"_ustr, cpo::uno::Any(14.0));

    saveAndReload(TestFilter::XLSX);

    xmlDocUniquePtr pXmlDoc = parseExport(u"xl/charts/chartEx1.xml"_ustr);
    CPPUNIT_ASSERT(pXmlDoc);

    static constexpr char sBinSize[]
        = "/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series/cx:layoutPr/"
          "cx:binning/cx:binSize";

    assertXPath(pXmlDoc, sBinSize, "val", u"2");

    static constexpr char sBinning[]
        = "/cx:chartSpace/cx:chart/cx:plotArea/cx:plotAreaRegion/cx:series/cx:layoutPr/"
          "cx:binning";

    assertXPath(pXmlDoc, sBinning, "underflow", u"11");
    assertXPath(pXmlDoc, sBinning, "overflow", u"14");

    xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<chart2::XAxis> xXAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xXAxis.is());

    chart2::ScaleData aScaleData = xXAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());

    Reference<chart2::data::XTextualDataSequence> xAxisCatText(aScaleData.Categories->getValues(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xAxisCatText.is());

    const Sequence<OUString> aAxisBinLabels = xAxisCatText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aAxisBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"<= 11"_ustr, aAxisBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(11-13]"_ustr, aAxisBinLabels[1]);
    CPPUNIT_ASSERT_EQUAL(u"(13-14]"_ustr, aAxisBinLabels[2]);
    CPPUNIT_ASSERT_EQUAL(u"> 14"_ustr, aAxisBinLabels[3]);

    Reference<beans::XPropertySet> xReloadedProps(xChartType, uno::UNO_QUERY_THROW);

    bool bUseUnderflowBin = false;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"UseUnderflowBin"_ustr) >>= bUseUnderflowBin);
    CPPUNIT_ASSERT(bUseUnderflowBin);

    double fUnderflowBinValue = 0.0;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"UnderflowBinValue"_ustr)
                   >>= fUnderflowBinValue);
    CPPUNIT_ASSERT_EQUAL(11.0, fUnderflowBinValue);

    bool bUseOverflowBin = false;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"UseOverflowBin"_ustr) >>= bUseOverflowBin);
    CPPUNIT_ASSERT(bUseOverflowBin);

    double fOverflowBinValue = 0.0;
    CPPUNIT_ASSERT(xReloadedProps->getPropertyValue(u"OverflowBinValue"_ustr)
                   >>= fOverflowBinValue);
    CPPUNIT_ASSERT_EQUAL(14.0, fOverflowBinValue);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testHistogramOOXMLUnderflowOverflowImport)
{
    loadFromFile(u"xlsx/tdf163727_histogram_underflow_overflow.xlsx");

    uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<chart2::XChartType> xChartType = getChartTypeFromDoc(xChartDoc, 0, 0);
    CPPUNIT_ASSERT(xChartType.is());

    Reference<beans::XPropertySet> xProps(xChartType, uno::UNO_QUERY_THROW);

    bool bUseUnderflowBin = false;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"UseUnderflowBin"_ustr) >>= bUseUnderflowBin);
    CPPUNIT_ASSERT(bUseUnderflowBin);

    double fUnderflowBinValue = 0.0;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"UnderflowBinValue"_ustr) >>= fUnderflowBinValue);
    CPPUNIT_ASSERT_EQUAL(11.0, fUnderflowBinValue);

    bool bUseOverflowBin = false;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"UseOverflowBin"_ustr) >>= bUseOverflowBin);
    CPPUNIT_ASSERT(bUseOverflowBin);

    double fOverflowBinValue = 0.0;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"OverflowBinValue"_ustr) >>= fOverflowBinValue);
    CPPUNIT_ASSERT_EQUAL(14.0, fOverflowBinValue);

    sal_Int32 nFrequencyType = -1;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"FrequencyType"_ustr) >>= nFrequencyType);
    CPPUNIT_ASSERT_EQUAL(sal_Int32(1), nFrequencyType);

    double fBinWidth = 0.0;
    CPPUNIT_ASSERT(xProps->getPropertyValue(u"BinWidth"_ustr) >>= fBinWidth);
    CPPUNIT_ASSERT_EQUAL(2.0, fBinWidth);

    Reference<chart2::XAxis> xXAxis = getAxisFromDoc(xChartDoc, 0, 0, 0);
    CPPUNIT_ASSERT(xXAxis.is());

    chart2::ScaleData aScaleData = xXAxis->getScaleData();
    CPPUNIT_ASSERT(aScaleData.Categories.is());

    Reference<chart2::data::XTextualDataSequence> xAxisCatText(aScaleData.Categories->getValues(),
                                                               uno::UNO_QUERY);
    CPPUNIT_ASSERT(xAxisCatText.is());

    const Sequence<OUString> aAxisBinLabels = xAxisCatText->getTextualData();
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aAxisBinLabels.getLength());
    CPPUNIT_ASSERT_EQUAL(u"<= 11"_ustr, aAxisBinLabels[0]);
    CPPUNIT_ASSERT_EQUAL(u"(11-13]"_ustr, aAxisBinLabels[1]);
    CPPUNIT_ASSERT_EQUAL(u"(13-14]"_ustr, aAxisBinLabels[2]);
    CPPUNIT_ASSERT_EQUAL(u"> 14"_ustr, aAxisBinLabels[3]);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf60316)
{
    loadFromFile(u"pptx/tdf60316.pptx");

    // 1st chart
    Reference<chart2::XChartDocument> xChartDoc(getChartDocFromDrawImpress(0, 0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<beans::XPropertySet> xPropSet = xChartDoc->getPageBackground();
    CPPUNIT_ASSERT(xPropSet.is());
    drawing::FillStyle eStyle
        = xPropSet->getPropertyValue(u"FillStyle"_ustr).get<drawing::FillStyle>();
    CPPUNIT_ASSERT_EQUAL_MESSAGE(
        "'Automatic' chart background fill in pptx should be loaded as no fill (transparent).",
        drawing::FillStyle_NONE, eStyle);

    Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram();
    Reference<beans::XPropertySet> xWallPropSet = xDiagram->getWall();

    eStyle = xWallPropSet->getPropertyValue(u"FillStyle"_ustr).get<drawing::FillStyle>();
    CPPUNIT_ASSERT_EQUAL_MESSAGE(
        "Wall background for styles below 32 should be transparent in pptx",
        drawing::FillStyle_NONE, eStyle);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf136754)
{
    loadFromFile(u"pptx/tdf136754.pptx");

    // 1st chart
    Reference<chart2::XChartDocument> xChartDoc(getChartDocFromDrawImpress(0, 0), uno::UNO_QUERY);
    CPPUNIT_ASSERT(xChartDoc.is());

    Reference<beans::XPropertySet> xPropSet = xChartDoc->getPageBackground();
    CPPUNIT_ASSERT(xPropSet.is());
    drawing::FillStyle eStyle
        = xPropSet->getPropertyValue(u"FillStyle"_ustr).get<drawing::FillStyle>();
    CPPUNIT_ASSERT_EQUAL_MESSAGE(
        "'Automatic' chart background fill in pptx should be loaded as no fill (transparent).",
        drawing::FillStyle_NONE, eStyle);

    Reference<chart2::XDiagram> xDiagram = xChartDoc->getFirstDiagram();
    Reference<beans::XPropertySet> xWallPropSet = xDiagram->getWall();
    eStyle = xWallPropSet->getPropertyValue(u"FillStyle"_ustr).get<drawing::FillStyle>();
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Wall background for styles above 32 should be solid in pptx",
                                 drawing::FillStyle_SOLID, eStyle);
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, testTdf94259)
{
    loadFromFile(u"xls/testRotatedAxisTitlePositions.xls");
    {
        uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
        CPPUNIT_ASSERT(xChartDoc.is());

        Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 1, 0);
        CPPUNIT_ASSERT(xAxis.is());
        lcl_assertAngles(xAxis, 0, 90);
        Reference<chart2::XTitle> xAxisTitle
            = css::uno::Reference<chart2::XTitled>(xAxis, UNO_QUERY_THROW)->getTitleObject();
        Reference<beans::XPropertySet> xAxisTitleProps(xAxisTitle, UNO_QUERY_THROW);

        chart2::RelativePosition aRelPos;
        CPPUNIT_ASSERT(xAxisTitleProps->getPropertyValue(u"RelativePosition"_ustr) >>= aRelPos);

        // old value with my setup 0.0327998, new value: 0.822488
        CPPUNIT_ASSERT_GREATER(0.1, aRelPos.Secondary);
        SAL_WARN("chart2", "Primary: " << aRelPos.Primary << ", " << aRelPos.Secondary);
    }
    {
        uno::Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(1);
        CPPUNIT_ASSERT(xChartDoc.is());

        Reference<chart2::XAxis> xAxis = getAxisFromDoc(xChartDoc, 0, 1, 0);
        CPPUNIT_ASSERT(xAxis.is());
        lcl_assertAngles(xAxis, 0, 270);

        Reference<chart2::XTitle> xAxisTitle
            = css::uno::Reference<chart2::XTitled>(xAxis, UNO_QUERY_THROW)->getTitleObject();
        Reference<beans::XPropertySet> xAxisTitleProps(xAxisTitle, UNO_QUERY_THROW);

        chart2::RelativePosition aRelPos;
        CPPUNIT_ASSERT(xAxisTitleProps->getPropertyValue(u"RelativePosition"_ustr) >>= aRelPos);

        SAL_WARN("chart2", "Primary: " << aRelPos.Primary << ", " << aRelPos.Secondary);
        // old value with my setup 0.0330348, new value: 0.0987283
        CPPUNIT_ASSERT_GREATER(0.05, aRelPos.Primary);
    }
}

CPPUNIT_TEST_FIXTURE(Chart2ImportTest2, TestParetoDefaultColor)
{
    loadFromFile(u"xlsx/paretoLine.xlsx");
    Reference<chart2::XChartDocument> xChartDoc = getChartDocFromSheet(0);
    CPPUNIT_ASSERT_MESSAGE("failed to load chart", xChartDoc.is());
    CPPUNIT_ASSERT(xChartDoc.is());
    // First series is clusteredColumn, second is paretoLine
    Reference<chart2::XDataSeries> xDataSeries = getDataSeriesFromDoc(xChartDoc, 0, 1);
    CPPUNIT_ASSERT(xDataSeries.is());
    Reference<beans::XPropertySet> xPropSet(xDataSeries, uno::UNO_QUERY_THROW);

    // Fill should be the second theme color
    cpo::uno::Any aAny = xPropSet->getPropertyValue(u"FillStyle"_ustr);
    com::sun::star::drawing::FillStyle aFillStyle(com::sun::star::drawing::FillStyle_NONE);
    CPPUNIT_ASSERT(aAny >>= aFillStyle);
    CPPUNIT_ASSERT_EQUAL(com::sun::star::drawing::FillStyle_SOLID, aFillStyle);

    aAny = xPropSet->getPropertyValue(u"FillColor"_ustr);
    sal_uInt32 nFillColor = 0;
    CPPUNIT_ASSERT(aAny >>= nFillColor);
    const sal_uInt32 nCorrectColor = 0xED7D31u;
    CPPUNIT_ASSERT_EQUAL(nCorrectColor, nFillColor);
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
