From 007cc77f298c975b85ad549f16f73232de6fec8a Mon Sep 17 00:00:00 2001 From: Balazs Varga Date: Fri, 26 Jun 2026 18:25:02 +0200 Subject: [PATCH] sc ooxml: don't write empty dxf border sides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A dxf holds only incremental formatting that is layered on top of an object's existing format. So an absent border side is left as-is, while an empty side element (e.g. ) defaults to style="none" and clears that border. Writing every side unconditionally made a headerRow that only defines a bottom border also clear the left/right/top borders it should inherit from the wholeTable element (based on ooxml standard). (only visible after re-importing into Excel) Change-Id: Idb703badb7649f7f91d57324295b39f68ec234c1 Signed-off-by: Balazs Varga Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5591 Tested-by: Jenkins CPCI Reviewed-by: Tomaž Vajngerl (cherry picked from commit cc0181aec07f66847b9c5a06cd047d50abc5a70c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207623 Tested-by: Jenkins Reviewed-by: Xisco Fauli --- sc/qa/unit/subsequent_export_test6.cxx | 14 ++++++++++++++ sc/source/filter/excel/xestyle.cxx | 24 ++++++++++++++---------- sc/source/filter/inc/xestyle.hxx | 3 ++- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/sc/qa/unit/subsequent_export_test6.cxx b/sc/qa/unit/subsequent_export_test6.cxx index 79c3674a1fdc..223995bc62c1 100644 --- a/sc/qa/unit/subsequent_export_test6.cxx +++ b/sc/qa/unit/subsequent_export_test6.cxx @@ -224,6 +224,20 @@ CPPUNIT_TEST_FIXTURE(ScExportTest6, testTableStyleInnerBordersExportXLSX) assertXPath(pStyles, sBorder + "/x:vertical/x:color", "theme", u"8"); assertXPath(pStyles, sBorder + "/x:horizontal", "style", u"thin"); assertXPath(pStyles, sBorder + "/x:horizontal/x:color", "theme", u"8"); + + sal_Int32 nHeaderRowDxfId + = getXPath( + pStyles, + "/x:styleSheet/x:tableStyles/x:tableStyle/x:tableStyleElement[@type='headerRow']", + "dxfId") + .toInt32(); + CPPUNIT_ASSERT(nHeaderRowDxfId >= 0); + OString sHdrBorder + = "/x:styleSheet/x:dxfs/x:dxf[" + OString::number(nHeaderRowDxfId + 1) + "]/x:border"; + assertXPath(pStyles, sHdrBorder + "/x:bottom", "style", u"medium"); + assertXPath(pStyles, sHdrBorder + "/x:left", 0); + assertXPath(pStyles, sHdrBorder + "/x:right", 0); + assertXPath(pStyles, sHdrBorder + "/x:top", 0); } CPPUNIT_TEST_FIXTURE(ScExportTest6, testTableStyleCustomRoundtripXLSX) diff --git a/sc/source/filter/excel/xestyle.cxx b/sc/source/filter/excel/xestyle.cxx index cf55dce8369d..782114d5b736 100644 --- a/sc/source/filter/excel/xestyle.cxx +++ b/sc/source/filter/excel/xestyle.cxx @@ -1869,12 +1869,16 @@ static const char* ToLineStyle( sal_uInt8 nLineStyle ) namespace { -void lcl_WriteBorder(XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLineStyle, const Color& rColor, model::ComplexColor const& rComplexColor) +void lcl_WriteBorder(XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLineStyle, const Color& rColor, model::ComplexColor const& rComplexColor, bool bWriteEmpty = true) { sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream(); if( nLineStyle == EXC_LINE_NONE ) { - rStyleSheet->singleElement(nElement); + // In a differential format (dxf) an empty side element (e.g. ) means + // "no border" and overrides the underlying table style, so omit it there and + // let the side be inherited. The cell border list still writes it. + if (bWriteEmpty) + rStyleSheet->singleElement(nElement); return; } else if (rColor == Color(0, 0, 0) && !rComplexColor.isValidThemeType()) @@ -1889,7 +1893,7 @@ void lcl_WriteBorder(XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLine } } // end anonymous namespace -void XclExpCellBorder::SaveXml(XclExpXmlStream& rStream) const +void XclExpCellBorder::SaveXml(XclExpXmlStream& rStream, bool bDxf) const { sax_fastparser::FSHelperPtr& rStyleSheet = rStream.GetCurrentStream(); @@ -1901,11 +1905,11 @@ void XclExpCellBorder::SaveXml(XclExpXmlStream& rStream) const // OOXTODO: XML_outline ); - lcl_WriteBorder(rStream, XML_left, mnLeftLine, rPalette.GetColor(mnLeftColor), maComplexColorLeft); - lcl_WriteBorder(rStream, XML_right, mnRightLine, rPalette.GetColor(mnRightColor), maComplexColorRight); - lcl_WriteBorder(rStream, XML_top, mnTopLine, rPalette.GetColor(mnTopColor), maComplexColorTop); - lcl_WriteBorder(rStream, XML_bottom, mnBottomLine, rPalette.GetColor(mnBottomColor), maComplexColorBottom); - lcl_WriteBorder(rStream, XML_diagonal, mnDiagLine, rPalette.GetColor(mnDiagColor), maComplexColorDiagonal); + lcl_WriteBorder(rStream, XML_left, mnLeftLine, rPalette.GetColor(mnLeftColor), maComplexColorLeft, !bDxf); + lcl_WriteBorder(rStream, XML_right, mnRightLine, rPalette.GetColor(mnRightColor), maComplexColorRight, !bDxf); + lcl_WriteBorder(rStream, XML_top, mnTopLine, rPalette.GetColor(mnTopColor), maComplexColorTop, !bDxf); + lcl_WriteBorder(rStream, XML_bottom, mnBottomLine, rPalette.GetColor(mnBottomColor), maComplexColorBottom, !bDxf); + lcl_WriteBorder(rStream, XML_diagonal, mnDiagLine, rPalette.GetColor(mnDiagColor), maComplexColorDiagonal, !bDxf); // Inner borders (vertical/horizontal). Only emitted when actually set, so // ordinary cell borders keep their previous output. @@ -3350,7 +3354,7 @@ void XclExpDxf::SaveXml( XclExpXmlStream& rStrm ) if (mpAlign) mpAlign->SaveXml(rStrm); if (mpBorder) - mpBorder->SaveXml(rStrm); + mpBorder->SaveXml(rStrm, /*bDxf*/true); if (mpProt) mpProt->SaveXml(rStrm); if (mpCellArea) @@ -3372,7 +3376,7 @@ void XclExpDxf::SaveXmlExt( XclExpXmlStream& rStrm ) if (mpAlign) mpAlign->SaveXml(rStrm); if (mpBorder) - mpBorder->SaveXml(rStrm); + mpBorder->SaveXml(rStrm, /*bDxf*/true); if (mpProt) mpProt->SaveXml(rStrm); rStyleSheet->endElementNS( XML_x14, XML_dxf ); diff --git a/sc/source/filter/inc/xestyle.hxx b/sc/source/filter/inc/xestyle.hxx index 5985572b6017..db87a8822e87 100644 --- a/sc/source/filter/inc/xestyle.hxx +++ b/sc/source/filter/inc/xestyle.hxx @@ -382,7 +382,8 @@ struct XclExpCellBorder : public XclCellBorder /** Fills the data to the passed fields of a BIFF8 CF (conditional format) record. */ void FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const; - void SaveXml( XclExpXmlStream& rStrm ) const; + /// bDxf: omit unset border sides (dxf) instead of writing them as empty elements. + void SaveXml( XclExpXmlStream& rStrm, bool bDxf = false ) const; }; /** Extends the XclCellArea struct for export.