chart2/qa/extras/chart2export3.cxx            |   22 +++++++++
 chart2/qa/extras/data/odt/testEmptyCharts.odt |binary
 oox/source/export/chartexport.cxx             |   63 ++++++++++++++++----------
 3 files changed, 61 insertions(+), 24 deletions(-)

New commits:
commit 15350f4d89e2bdf681c35d9b20a648bbdd6d4240
Author:     Aron Budea <[email protected]>
AuthorDate: Thu Oct 16 20:17:44 2025 +1030
Commit:     Thorsten Behrens <[email protected]>
CommitDate: Wed Nov 12 03:41:29 2025 +0100

    tdf#168885 oox: save more of empty charts
    
    Otherwise Word will complain.
    Affected types: area, bar, bubble, line, stock.
    
    Regression from 3b4c11350a631e27345e87ecfe258d12983cbfbc.
    
    Change-Id: I56d533d9c55bae45240a1b51733ff60f47b1b2e7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192483
    Reviewed-by: Aron Budea <[email protected]>
    Tested-by: Jenkins
    Reviewed-by: Balazs Varga <[email protected]>
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192782
    (cherry picked from commit 30ca1deeebbdb3cf69816fe65401d91e36596717)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193798
    Reviewed-by: Thorsten Behrens <[email protected]>
    Tested-by: Thorsten Behrens <[email protected]>

diff --git a/chart2/qa/extras/chart2export3.cxx 
b/chart2/qa/extras/chart2export3.cxx
index 838da77191b7..92d0f528231a 100644
--- a/chart2/qa/extras/chart2export3.cxx
+++ b/chart2/qa/extras/chart2export3.cxx
@@ -737,6 +737,28 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, 
testBarChartSecondaryAxisXLSX)
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:plotArea/c:valAx[2]/c:axId"_ostr, "val"_ostr, 
YValueIdOf2Barchart);
 }
 
+CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testEmptyCharts)
+{
+    loadFromFile(u"odt/testEmptyCharts.odt");
+    save(u"Office Open XML Text"_ustr);
+
+    // Make sure each chart exists in the respective XML
+    xmlDocUniquePtr pXmlDoc = parseExport(u"word/charts/chart1.xml"_ustr);
+    assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:barChart");
+
+    pXmlDoc = parseExport(u"word/charts/chart2.xml"_ustr);
+    assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:areaChart");
+
+    pXmlDoc = parseExport(u"word/charts/chart3.xml"_ustr);
+    assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:bubbleChart");
+
+    pXmlDoc = parseExport(u"word/charts/chart4.xml"_ustr);
+    assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:lineChart");
+
+    pXmlDoc = parseExport(u"word/charts/chart5.xml"_ustr);
+    assertXPath(pXmlDoc, "/c:chartSpace/c:chart/c:plotArea/c:stockChart");
+}
+
 CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testTdf148142)
 {
     // The document contains a line chart with "Between tick marks" X axis 
position.
diff --git a/chart2/qa/extras/data/odt/testEmptyCharts.odt 
b/chart2/qa/extras/data/odt/testEmptyCharts.odt
new file mode 100644
index 000000000000..54bf12e85763
Binary files /dev/null and b/chart2/qa/extras/data/odt/testEmptyCharts.odt 
differ
diff --git a/oox/source/export/chartexport.cxx 
b/oox/source/export/chartexport.cxx
index c80e8c1ba688..b25b9fd764f4 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -2091,12 +2091,14 @@ void ChartExport::exportDataTable( )
 void ChartExport::exportAreaChart( const Reference< chart2::XChartType >& 
xChartType )
 {
     FSHelperPtr pFS = GetFS();
-    const std::vector<Sequence<Reference<chart2::XDataSeries> > > 
aSplitDataSeries = splitDataSeriesByAxis(xChartType);
+    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = 
splitDataSeriesByAxis(xChartType);
+    if (aSplitDataSeries.empty())
+    {
+        // Use a dummy data series to output needed basic chart-related XML 
even in case of empty charts
+        aSplitDataSeries.push_back({});
+    }
     for (const auto& splitDataSeries : aSplitDataSeries)
     {
-        if (!splitDataSeries.hasElements())
-            continue;
-
         sal_Int32 nTypeId = XML_areaChart;
         if (mbIs3DChart)
             nTypeId = XML_area3DChart;
@@ -2104,7 +2106,8 @@ void ChartExport::exportAreaChart( const Reference< 
chart2::XChartType >& xChart
 
         exportGrouping();
         bool bPrimaryAxes = true;
-        exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
+        if (splitDataSeries.hasElements())
+            exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
         exportAxesId(bPrimaryAxes);
 
         pFS->endElement(FSNS(XML_c, nTypeId));
@@ -2118,12 +2121,14 @@ void ChartExport::exportBarChart(const Reference< 
chart2::XChartType >& xChartTy
         nTypeId = XML_bar3DChart;
     FSHelperPtr pFS = GetFS();
 
-    const std::vector<Sequence<Reference<chart2::XDataSeries> > > 
aSplitDataSeries = splitDataSeriesByAxis(xChartType);
+    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = 
splitDataSeriesByAxis(xChartType);
+    if (aSplitDataSeries.empty())
+    {
+        // Use a dummy data series to output needed basic chart-related XML 
even in case of empty charts
+        aSplitDataSeries.push_back({});
+    }
     for (const auto& splitDataSeries : aSplitDataSeries)
     {
-        if (!splitDataSeries.hasElements())
-            continue;
-
         pFS->startElement(FSNS(XML_c, nTypeId));
         // bar direction
         bool bVertical = false;
@@ -2139,6 +2144,7 @@ void ChartExport::exportBarChart(const Reference< 
chart2::XChartType >& xChartTy
         exportVaryColors(xChartType);
 
         bool bPrimaryAxes = true;
+        if (splitDataSeries.hasElements())
         exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
 
         Reference< XPropertySet > xTypeProp(xChartType, uno::UNO_QUERY);
@@ -2214,18 +2220,21 @@ void ChartExport::exportBarChart(const Reference< 
chart2::XChartType >& xChartTy
 void ChartExport::exportBubbleChart( const Reference< chart2::XChartType >& 
xChartType )
 {
     FSHelperPtr pFS = GetFS();
-    const std::vector<Sequence<Reference<chart2::XDataSeries> > > 
aSplitDataSeries = splitDataSeriesByAxis(xChartType);
+    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = 
splitDataSeriesByAxis(xChartType);
+    if (aSplitDataSeries.empty())
+    {
+        // Use a dummy data series to output needed basic chart-related XML 
even in case of empty charts
+        aSplitDataSeries.push_back({});
+    }
     for (const auto& splitDataSeries : aSplitDataSeries)
     {
-        if (!splitDataSeries.hasElements())
-            continue;
-
         pFS->startElement(FSNS(XML_c, XML_bubbleChart));
 
         exportVaryColors(xChartType);
 
         bool bPrimaryAxes = true;
-        exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
+        if (splitDataSeries.hasElements())
+            exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
 
         exportAxesId(bPrimaryAxes);
 
@@ -2290,12 +2299,14 @@ void writeDataLabelsRange(const FSHelperPtr& pFS, const 
XmlFilterBase* pFB, Data
 void ChartExport::exportLineChart( const Reference< chart2::XChartType >& 
xChartType )
 {
     FSHelperPtr pFS = GetFS();
-    const std::vector<Sequence<Reference<chart2::XDataSeries> > > 
aSplitDataSeries = splitDataSeriesByAxis(xChartType);
+    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = 
splitDataSeriesByAxis(xChartType);
+    if (aSplitDataSeries.empty())
+    {
+        // Use a dummy data series to output needed basic chart-related XML 
even in case of empty charts
+        aSplitDataSeries.push_back({});
+    }
     for (const auto& splitDataSeries : aSplitDataSeries)
     {
-        if (!splitDataSeries.hasElements())
-            continue;
-
         sal_Int32 nTypeId = XML_lineChart;
         if( mbIs3DChart )
             nTypeId = XML_line3DChart;
@@ -2306,7 +2317,8 @@ void ChartExport::exportLineChart( const Reference< 
chart2::XChartType >& xChart
         exportVaryColors(xChartType);
         // TODO: show marker symbol in series?
         bool bPrimaryAxes = true;
-        exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
+        if (splitDataSeries.hasElements())
+            exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
 
         // show marker?
         sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
@@ -2427,16 +2439,19 @@ void ChartExport::exportScatterChart( const Reference< 
chart2::XChartType >& xCh
 void ChartExport::exportStockChart( const Reference< chart2::XChartType >& 
xChartType )
 {
     FSHelperPtr pFS = GetFS();
-    const std::vector<Sequence<Reference<chart2::XDataSeries> > > 
aSplitDataSeries = splitDataSeriesByAxis(xChartType);
+    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = 
splitDataSeriesByAxis(xChartType);
+    if (aSplitDataSeries.empty())
+    {
+        // Use a dummy data series to output needed basic chart-related XML 
even in case of empty charts
+        aSplitDataSeries.push_back({});
+    }
     for (const auto& splitDataSeries : aSplitDataSeries)
     {
-        if (!splitDataSeries.hasElements())
-            continue;
-
         pFS->startElement(FSNS(XML_c, XML_stockChart));
 
         bool bPrimaryAxes = true;
-        exportCandleStickSeries(splitDataSeries, bPrimaryAxes);
+        if (splitDataSeries.hasElements())
+            exportCandleStickSeries(splitDataSeries, bPrimaryAxes);
 
         // export stock properties
         Reference< css::chart::XStatisticDisplay > 
xStockPropProvider(mxDiagram, uno::UNO_QUERY);

Reply via email to