sc/source/core/tool/dbdata.cxx | 20 ++++++++++++++++++++ sc/source/filter/excel/xedbdata.cxx | 14 ++++++++++++++ sc/source/filter/excel/xetable.cxx | 31 +++++++++++++++++++++++++++---- sc/source/filter/inc/xedbdata.hxx | 1 + 4 files changed, 62 insertions(+), 4 deletions(-)
New commits: commit b16dd6621e1eeeb15272f2bfb64e2e73c8ffa4c5 Author: Szymon Kłos <[email protected]> AuthorDate: Thu Feb 19 12:57:19 2026 +0000 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Fri Feb 20 11:08:39 2026 +0100 xlsx export: numeric table headers use shared strings When a structured table header cell contains a number (e.g. "1" or "2025"), the xlsx export was writing it as a numeric cell without the t="s" type attribute. This caused a mismatch between the table column name in table1.xml and the cell value in the sheet XML. - RefreshTableColumnNames() now converts numeric header cells to their formatted string representation, so table.xml gets the actual value (e.g. "1") instead of auto-generating "Column1". - In XclExpCellTable, numeric cells in table header rows are now exported as XclExpLabelCell (shared string with t="s") instead of XclExpNumberCell, using the already-initialized XclExpTablesManager to detect header positions. - Removed the explicit t="n" type attribute from XclExpNumberCell and XclExpRkCell, as "n" is the default cell type This prevents showing an error when file is opened in MSO. Change-Id: I24d68cc3c802fede8cfc029b4a829ac9748da227 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199791 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Tomaž Vajngerl <[email protected]> diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx index 6d4ee3e2e518..f1acb9c241fe 100644 --- a/sc/source/core/tool/dbdata.cxx +++ b/sc/source/core/tool/dbdata.cxx @@ -1509,6 +1509,26 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc ) } nLastColFilled = nCol; } + else if (pCell->getType() == CELLTYPE_VALUE) + { + // Numeric header: convert to string so the column name + // in table.xml matches the shared string exported for + // this cell. + double fValue = pCell->getDouble(); + sal_uInt32 nFormat = pDoc->GetNumberFormat(nCol, nRow, nTable); + OUString aStr; + const Color* pColor = nullptr; + pDoc->GetFormatTable()->GetOutputString(fValue, nFormat, aStr, &pColor); + if (aStr.isEmpty()) + bHaveEmpty = true; + else + { + SetTableColumnName( aNewNames, nCol-nStartCol, aStr, 0); + if (nLastColFilled < nCol-1) + bHaveEmpty = true; + } + nLastColFilled = nCol; + } else bHaveEmpty = true; } diff --git a/sc/source/filter/excel/xedbdata.cxx b/sc/source/filter/excel/xedbdata.cxx index 522b5ea19747..9bc3e7f2bdef 100644 --- a/sc/source/filter/excel/xedbdata.cxx +++ b/sc/source/filter/excel/xedbdata.cxx @@ -176,6 +176,20 @@ void XclExpTables::AppendTable( const ScDBData* pData, sal_Int32 nTableId ) maTables.emplace_back( pData, nTableId); } +void XclExpTables::GetHeaderRows( ::std::vector<ScRange>& rRanges ) const +{ + for (const auto& rEntry : maTables) + { + if (rEntry.mpData->HasHeader()) + { + ScRange aArea; + rEntry.mpData->GetArea(aArea); + aArea.aEnd.SetRow(aArea.aStart.Row()); + rRanges.push_back(aArea); + } + } +} + void XclExpTables::SaveTableXml( XclExpXmlStream& rStrm, const Entry& rEntry ) { const ScDBData& rData = *rEntry.mpData; diff --git a/sc/source/filter/excel/xetable.cxx b/sc/source/filter/excel/xetable.cxx index 5e8bc8a3ffda..eeccbe215c22 100644 --- a/sc/source/filter/excel/xetable.cxx +++ b/sc/source/filter/excel/xetable.cxx @@ -34,6 +34,7 @@ #include <formulacell.hxx> #include <patattr.hxx> #include <attrib.hxx> +#include <xedbdata.hxx> #include <xehelper.hxx> #include <xecontent.hxx> #include <xeescher.hxx> @@ -646,8 +647,7 @@ void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm ) sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); rWorksheet->startElement( XML_c, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(), - XML_s, lcl_GetStyleId(rStrm, *this), - XML_t, "n" + XML_s, lcl_GetStyleId(rStrm, *this) // OOXTODO: XML_cm, XML_vm, XML_ph ); rWorksheet->startElement(XML_v); @@ -1427,8 +1427,7 @@ void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& r sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream(); rWorksheet->startElement( XML_c, XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(), - XML_s, lcl_GetStyleId(rStrm, nXFId), - XML_t, "n" + XML_s, lcl_GetStyleId(rStrm, nXFId) // OOXTODO: XML_cm, XML_vm, XML_ph ); rWorksheet->startElement( XML_v ); @@ -2626,6 +2625,16 @@ XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) : SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow ); ScUsedAreaIterator aIt( rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow ); + // Collect table header ranges for the current sheet so that numeric + // cells in table headers are exported as shared strings (not numbers). + std::vector<ScRange> aTableHeaderRanges; + if (GetOutput() == EXC_OUTPUT_XML_2007) + { + rtl::Reference<XclExpTables> xTables = GetTablesManager().GetTablesBySheet(nScTab); + if (xTables) + xTables->GetHeaderRows(aTableHeaderRanges); + } + // activate the correct segment and sub segment at the progress bar GetProgressBar().ActivateCreateRowsSegment(); @@ -2668,6 +2677,20 @@ XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) : { double fValue = rScCell.getDouble(); + // If the cell is in a table header row, force export as shared string + if (std::any_of(aTableHeaderRanges.begin(), aTableHeaderRanges.end(), + [&aScPos](const ScRange& rRange) { return rRange.Contains(aScPos); })) + { + OUString aStr; + const Color* pColor = nullptr; + sal_uInt32 nScNumFmt = pPattern + ? pPattern->GetItem(ATTR_VALUE_FORMAT).GetValue() : 0; + rFormatter.GetOutputString(fValue, nScNumFmt, aStr, &pColor); + xCell = new XclExpLabelCell( + GetRoot(), aXclPos, pPattern, nMergeBaseXFId, aStr); + break; + } + if (pPattern) { OUString aUrl = pPattern->GetItem(ATTR_HYPERLINK).GetValue(); diff --git a/sc/source/filter/inc/xedbdata.hxx b/sc/source/filter/inc/xedbdata.hxx index a7fddfe04eaa..24df0fffcb67 100644 --- a/sc/source/filter/inc/xedbdata.hxx +++ b/sc/source/filter/inc/xedbdata.hxx @@ -31,6 +31,7 @@ public: virtual ~XclExpTables() override; void AppendTable( const ScDBData* pData, sal_Int32 nTableId ); + void GetHeaderRows( ::std::vector<ScRange>& rRanges ) const; protected: struct Entry
