tdf#114166 DOCX chart import: fix missing complex categories
Now complex category labels are visible, and the inner data
table contains the correct texts of the category columns.
Note: repeating call of createDataSequenceByValueArray() API function
can create all columns of the complex categories. See also
commit 6c4e21a234f12e1310ba06f9859e08b424acf8bf
"bnc#812796: Correctly handle static value array for OOXML charts."
Change-Id: I333b79be35a24a912bb9e662116d0c85809a8fb2
Reviewed-on: https://gerrit.libreoffice.org/75776
Reviewed-by: László Németh <nemeth@numbertext.org>
Tested-by: László Németh <nemeth@numbertext.org>
diff --git a/chart2/qa/extras/chart2import.cxx b/chart2/qa/extras/chart2import.cxx
index 107daed..710d15e 100644
--- a/chart2/qa/extras/chart2import.cxx
+++ b/chart2/qa/extras/chart2import.cxx
@@ -131,6 +131,7 @@ public:
void testDataPointInheritedColorDOCX();
void testExternalStrRefsXLSX();
void testSourceNumberFormatComplexCategoriesXLS();
void testMultilevelCategoryAxis();
void testTdf123504();
void testTdf122765();
@@ -216,6 +217,7 @@ public:
CPPUNIT_TEST(testDataPointInheritedColorDOCX);
CPPUNIT_TEST(testExternalStrRefsXLSX);
CPPUNIT_TEST(testSourceNumberFormatComplexCategoriesXLS);
CPPUNIT_TEST(testMultilevelCategoryAxis);
CPPUNIT_TEST(testTdf123504);
CPPUNIT_TEST(testTdf122765);
@@ -1534,10 +1536,10 @@ void Chart2ImportTest::testInternalDataProvider() {
// Parse mixed types, mixed role
xDataSeq = rxDataProvider->createDataSequenceByValueArray("categories", "{42;\"hello\";0;\"world\"}");
xSequence = xDataSeq->getData();
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("42")), xSequence[0]);
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("hello")), xSequence[1]);
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("0")), xSequence[2]);
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("world")), xSequence[3]);
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("Row 1 42")), xSequence[0]);
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("Row 2 hello")), xSequence[1]);
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("Row 3 0")), xSequence[2]);
CPPUNIT_ASSERT_EQUAL(uno::Any(OUString("Row 4 world")), xSequence[3]);
}
void Chart2ImportTest::testTdf90510()
@@ -1946,6 +1948,34 @@ void Chart2ImportTest::testSourceNumberFormatComplexCategoriesXLS()
CPPUNIT_ASSERT(nNumberFormat != 0);
}
void Chart2ImportTest::testMultilevelCategoryAxis()
{
load("/chart2/qa/extras/data/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(OUString("2011"), aCategories[0][0]);
CPPUNIT_ASSERT_EQUAL(OUString(""), aCategories[1][0]);
CPPUNIT_ASSERT_EQUAL(OUString("2012"), aCategories[2][0]);
CPPUNIT_ASSERT_EQUAL(OUString(""), aCategories[3][0]);
CPPUNIT_ASSERT_EQUAL(OUString("Categoria 1"), aCategories[0][1]);
CPPUNIT_ASSERT_EQUAL(OUString("Categoria 2"), aCategories[1][1]);
CPPUNIT_ASSERT_EQUAL(OUString("Categoria 3"), aCategories[2][1]);
CPPUNIT_ASSERT_EQUAL(OUString("Categoria 4"), aCategories[3][1]);
}
void Chart2ImportTest::testTdf123504()
{
load("/chart2/qa/extras/data/ods/", "pie_chart_100_and_0.ods");
diff --git a/chart2/qa/extras/data/docx/testMultilevelCategoryAxis.docx b/chart2/qa/extras/data/docx/testMultilevelCategoryAxis.docx
new file mode 100644
index 0000000..75605de
--- /dev/null
+++ b/chart2/qa/extras/data/docx/testMultilevelCategoryAxis.docx
Binary files differ
diff --git a/chart2/source/tools/InternalData.cxx b/chart2/source/tools/InternalData.cxx
index b99578f..b7a5118 100644
--- a/chart2/source/tools/InternalData.cxx
+++ b/chart2/source/tools/InternalData.cxx
@@ -220,7 +220,16 @@ void InternalData::setComplexRowLabel( sal_Int32 nRowIndex, const vector< uno::A
m_aRowLabels.resize(nRowIndex+1);
enlargeData( 0, nRowIndex+1 );
}
m_aRowLabels[nRowIndex] = rComplexLabel;
sal_Int32 nSize = static_cast<sal_Int32>( m_aRowLabels[nRowIndex].size() );
if( nSize >= 1 )
{
m_aRowLabels[nRowIndex].resize(nSize+1);
m_aRowLabels[nRowIndex][nSize] = rComplexLabel[0];
}
else
{
m_aRowLabels[nRowIndex] = rComplexLabel;
}
}
vector< uno::Any > InternalData::getComplexColumnLabel( sal_Int32 nColumnIndex ) const
diff --git a/include/oox/drawingml/chart/datasourcemodel.hxx b/include/oox/drawingml/chart/datasourcemodel.hxx
index 097bfd1..e2d9336 100644
--- a/include/oox/drawingml/chart/datasourcemodel.hxx
+++ b/include/oox/drawingml/chart/datasourcemodel.hxx
@@ -40,6 +40,7 @@ struct DataSequenceModel
OUString maFormula; /// Formula reference, e.g. into a spreadsheet.
OUString maFormatCode; /// Number format for double values.
sal_Int32 mnPointCount; /// Number of points in this series source.
sal_Int32 mnLevelCount; /// Number of category levels.
explicit DataSequenceModel();
~DataSequenceModel();
diff --git a/oox/source/drawingml/chart/chartconverter.cxx b/oox/source/drawingml/chart/chartconverter.cxx
index f86ea8a..538f995 100644
--- a/oox/source/drawingml/chart/chartconverter.cxx
+++ b/oox/source/drawingml/chart/chartconverter.cxx
@@ -56,12 +56,12 @@ static OUString lclGenerateApiString( const OUString& rString )
return "\"" + aRetString + "\"";
}
static OUString lclGenerateApiArray(const std::vector<Any>& rRow)
static OUString lclGenerateApiArray(const std::vector<Any>& rRow, sal_Int32 nStart, sal_Int32 nCount)
{
OSL_ENSURE( !rRow.empty(), "ChartConverter::lclGenerateApiArray - missing matrix values" );
OUStringBuffer aBuffer;
aBuffer.append( API_TOKEN_ARRAY_OPEN );
for (auto aBeg = rRow.begin(), aIt = aBeg, aEnd = rRow.end(); aIt != aEnd; ++aIt)
for (auto aBeg = rRow.begin() + nStart, aIt = aBeg, aEnd = aBeg + nCount; aIt != aEnd; ++aIt)
{
double fValue = 0.0;
OUString aString;
@@ -124,21 +124,26 @@ Reference< XDataSequence > ChartConverter::createDataSequence(
if( rxDataProvider.is() )
{
OUString aRangeRep;
if( !rDataSeq.maData.empty() )
if( !rDataSeq.maData.empty() ) try
{
// create a single-row array from constant source data
std::vector<Any> aRow(rDataSeq.mnPointCount);
// (multiple levels in the case of complex categories)
std::vector<Any> aRow(rDataSeq.mnLevelCount * rDataSeq.mnPointCount);
for (auto const& elem : rDataSeq.maData)
aRow.at(elem.first) = elem.second;
aRangeRep = lclGenerateApiArray(aRow);
}
for (sal_Int32 i = rDataSeq.mnLevelCount-1; i >= 0; i--)
{
aRangeRep = lclGenerateApiArray( aRow, i * rDataSeq.mnPointCount, rDataSeq.mnPointCount);
if( !aRangeRep.isEmpty() ) try
{
// create the data sequence
xDataSeq = rxDataProvider->createDataSequenceByValueArray(rRole, aRangeRep);
return xDataSeq;
if (!aRangeRep.isEmpty())
{
// create or add a new level to the data sequence
xDataSeq = rxDataProvider->createDataSequenceByValueArray(rRole, aRangeRep);
if (i == 0)
return xDataSeq;
}
}
}
catch( Exception& )
{
diff --git a/oox/source/drawingml/chart/datasourcecontext.cxx b/oox/source/drawingml/chart/datasourcecontext.cxx
index e50723b..7fa7b12 100644
--- a/oox/source/drawingml/chart/datasourcecontext.cxx
+++ b/oox/source/drawingml/chart/datasourcecontext.cxx
@@ -180,6 +180,7 @@ ContextHandlerRef StringSequenceContext::onCreateContext( sal_Int32 nElement, co
switch( nElement )
{
case C_TOKEN( f ):
case C_TOKEN( multiLvlStrCache ):
return this;
}
break;
@@ -206,6 +207,28 @@ ContextHandlerRef StringSequenceContext::onCreateContext( sal_Int32 nElement, co
}
break;
case C_TOKEN( multiLvlStrCache ):
switch (nElement)
{
case C_TOKEN( ptCount ):
mrModel.mnPointCount = rAttribs.getInteger(XML_val, -1);
mrModel.mnLevelCount--; // normalize level count
return nullptr;
case C_TOKEN( lvl ):
mrModel.mnLevelCount++;
return this;
}
break;
case C_TOKEN( lvl ):
switch (nElement)
{
case C_TOKEN(pt):
mnPtIndex = rAttribs.getInteger(XML_idx, -1);
return this;
}
break;
case C_TOKEN( pt ):
switch( nElement )
{
@@ -226,7 +249,7 @@ void StringSequenceContext::onCharacters( const OUString& rChars )
break;
case C_TOKEN( v ):
if( mnPtIndex >= 0 )
mrModel.maData[ mnPtIndex ] <<= rChars;
mrModel.maData[ (mrModel.mnLevelCount-1) * mrModel.mnPointCount + mnPtIndex ] <<= rChars;
break;
}
}
diff --git a/oox/source/drawingml/chart/datasourcemodel.cxx b/oox/source/drawingml/chart/datasourcemodel.cxx
index c5c054e..ecb9afe 100644
--- a/oox/source/drawingml/chart/datasourcemodel.cxx
+++ b/oox/source/drawingml/chart/datasourcemodel.cxx
@@ -24,7 +24,8 @@ namespace drawingml {
namespace chart {
DataSequenceModel::DataSequenceModel() :
mnPointCount( -1 )
mnPointCount( -1 ),
mnLevelCount( 1 )
{
}
diff --git a/oox/source/export/chartexport.cxx b/oox/source/export/chartexport.cxx
index dd9d196..ec19bc40 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -515,8 +515,12 @@ Sequence< Sequence< OUString > > ChartExport::getSplitCategoriesList( const OUSt
aFinalSplitSource[nLevelCount - i - 1].realloc(aAnyCategories.getLength());
for (auto const& elemLabel : aAnyCategories)
{
aFinalSplitSource[nLevelCount - i - 1][nElemLabel] = elemLabel[i].get<OUString>();
nElemLabel++;
// make sure elemLabel[i] exists!
if (elemLabel.getLength() > i)
{
aFinalSplitSource[nLevelCount - i - 1][nElemLabel] = elemLabel[i].get<OUString>();
nElemLabel++;
}
}
}
return aFinalSplitSource;