sc/inc/scextopt.hxx | 1 + sc/qa/unit/subsequent_export_test2.cxx | 27 ++++++++++++++++++++++++++- sc/source/filter/excel/excdoc.cxx | 17 +++++++++++++---- sc/source/filter/inc/workbooksettings.hxx | 5 +++++ sc/source/filter/oox/workbookfragment.cxx | 1 + sc/source/filter/oox/workbooksettings.cxx | 16 ++++++++++++++++ 6 files changed, 62 insertions(+), 5 deletions(-)
New commits: commit 70ec8eca720e3cdc0f1b26b14c9984ce691659cd Author: Justin Luth <[email protected]> AuthorDate: Wed Dec 10 16:31:34 2025 -0500 Commit: Justin Luth <[email protected]> CommitDate: Fri Dec 12 00:44:35 2025 +0100 tdf#165180 xlsx export: lowestEdited=4 for ECMA 376 1st ed. If we don't provide lowestEdited=4 when saving as an Excel 2007 spreadsheet, then LO (and Excel) will treat it as a 2010 document the next time it is loaded. make CppunitTest_sc_subsequent_export_test2 \ CPPUNIT_TEST_NAME=testTdf114969XLSX Change-Id: I7146c083dbc4eb451ce26cf1e282f63e6859c1b7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195180 Tested-by: Jenkins Reviewed-by: Justin Luth <[email protected]> diff --git a/sc/qa/unit/subsequent_export_test2.cxx b/sc/qa/unit/subsequent_export_test2.cxx index b38b6eefaf6f..5eaa901bb05b 100644 --- a/sc/qa/unit/subsequent_export_test2.cxx +++ b/sc/qa/unit/subsequent_export_test2.cxx @@ -1124,6 +1124,12 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf114969XLSX) CPPUNIT_ASSERT(pDoc); assertXPath(pDoc, "/x:worksheet/x:hyperlinks/x:hyperlink[1]", "location", u"'1.1.1.1'!C1"); assertXPath(pDoc, "/x:worksheet/x:hyperlinks/x:hyperlink[2]", "location", u"'1.1.1.1'!C2"); + + // tdf#165180: should be recognizable as a 2007 XLSX format when saved + save(TestFilter::XLSX_2007); + xmlDocUniquePtr pWorkbook = parseExport(u"xl/workbook.xml"_ustr); + // lowestEdited = 4 needs to be output in order to be detected as a 2007 ECMA_376_1ST_EDITION + assertXPath(pWorkbook, "/x:workbook/x:fileVersion", "lowestEdited", u"4"); } CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf115192XLSX) diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx index fc794b00ab60..21e537baa28f 100644 --- a/sc/source/filter/excel/excdoc.cxx +++ b/sc/source/filter/excel/excdoc.cxx @@ -857,6 +857,11 @@ void ExcDocument::WriteXml( XclExpXmlStream& rStrm ) = sax_fastparser::FastSerializerHelper::createAttrList(); pAttrListFileVersion->add(XML_appName, "Calc"); std::optional<sal_Int16> oLow = GetExtDocOptions().GetDocSettings().moLowestEdited; + if (rStrm.getVersion() == oox::core::ECMA_376_1ST_EDITION + && (!oLow.has_value() || oLow > 4)) + { + oLow = 4; // when saving as MS Excel 2007, set the corresponding lowestEdited-by version + } if (oLow.has_value()) pAttrListFileVersion->add(XML_lowestEdited, OString::number(*oLow)); // OOXTODO: XML_codeName commit 78db442150f7c729c6ab6b7dd7a23cedcace1ef8 Author: Justin Luth <[email protected]> AuthorDate: Sat Nov 29 17:08:59 2025 -0500 Commit: Justin Luth <[email protected]> CommitDate: Fri Dec 12 00:44:28 2025 +0100 tdf#165180 xlsx UI: round-trip as 2007 Spreadsheet, not 2010-365 Historically we have ALWAYS round-tripped with the 2007 filter. A recent change (in this patchset) started importing xlsx without a lowestEdited as 2010+ documents, and thus they will then also export as 2010+. With this patch, we round-trip the original lowestEdited, and thus we can again round-trip 2007 as 2007. make CppunitTest_sc_subsequent_export_test2 \ CPPUNIT_TEST_NAME=testMatrixMultiplicationXLSX Change-Id: I85c4163f76902f2972193a2fdfe7ee9b4dd510c0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195035 Tested-by: Jenkins Reviewed-by: Justin Luth <[email protected]> diff --git a/sc/inc/scextopt.hxx b/sc/inc/scextopt.hxx index 4a71fdd7dd30..1cc8b3f3bcd7 100644 --- a/sc/inc/scextopt.hxx +++ b/sc/inc/scextopt.hxx @@ -30,6 +30,7 @@ struct ScExtDocSettings double mfTabBarWidth; ///< Width of the tabbar, relative to frame window width (0.0 ... 1.0). sal_uInt32 mnLinkCnt; ///< Recursive counter for loading external documents. SCTAB mnDisplTab; ///< Index of displayed sheet. + std::optional<sal_Int16> moLowestEdited; ///< Oldest Excel version that edited this document. explicit ScExtDocSettings(); }; diff --git a/sc/qa/unit/subsequent_export_test2.cxx b/sc/qa/unit/subsequent_export_test2.cxx index 3753d531bbcf..b38b6eefaf6f 100644 --- a/sc/qa/unit/subsequent_export_test2.cxx +++ b/sc/qa/unit/subsequent_export_test2.cxx @@ -140,6 +140,13 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testMatrixMultiplicationXLSX) { createScDoc("xlsx/matrix-multiplication.xlsx"); + // tdf#165180: should be recognized as a 2007 XLSX format when loaded (and resaved...) + SfxMedium* pMedium = getScDocShell()->GetMedium(); + SfxFilterMatcher aMatcher(u"com.sun.star.sheet.SpreadsheetDocument"_ustr); + std::shared_ptr<const SfxFilter> pFilter; + aMatcher.DetectFilter(*pMedium, pFilter); + CPPUNIT_ASSERT_EQUAL(u"Calc MS Excel 2007 XML"_ustr, pFilter->GetFilterName()); + save(TestFilter::XLSX); xmlDocUniquePtr pDoc = parseExport(u"xl/worksheets/sheet1.xml"_ustr); @@ -154,6 +161,11 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testMatrixMultiplicationXLSX) // make sure that the CellFormulaType is array. CPPUNIT_ASSERT_EQUAL(u"array"_ustr, CellFormulaType); + + // tdf#165180: should be recognizable as a 2007 XLSX format when saved + xmlDocUniquePtr pWorkbook = parseExport(u"xl/workbook.xml"_ustr); + // lowestEdited = 4 needs to be round-tripped in order to detect as a 2007 ECMA_376_1ST_EDITION + assertXPath(pWorkbook, "/x:workbook/x:fileVersion", "lowestEdited", u"4"); } CPPUNIT_TEST_FIXTURE(ScExportTest2, testRefStringXLSX) @@ -1431,7 +1443,7 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf123645XLSX) CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf125173XLSX) { createScDoc("ods/text_box_hyperlink.ods"); - save(TestFilter::XLSX); + saveAndReload(TestFilter::XLSX); xmlDocUniquePtr pDoc = parseExport(u"xl/drawings/drawing1.xml"_ustr); CPPUNIT_ASSERT(pDoc); @@ -1443,6 +1455,13 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf125173XLSX) u"http://www.google.com/"); assertXPath(pXmlRels, "/rels:Relationships/rels:Relationship[@Id='rId1']", "TargetMode", u"External"); + + // tdf#137883: should be recognized as a 2010+ XLSX format when reloaded (and resaved...) + SfxMedium* pMedium = getScDocShell()->GetMedium(); + SfxFilterMatcher aMatcher(u"com.sun.star.sheet.SpreadsheetDocument"_ustr); + std::shared_ptr<const SfxFilter> pFilter; + aMatcher.DetectFilter(*pMedium, pFilter); + CPPUNIT_ASSERT_EQUAL(u"Calc Office Open XML"_ustr, pFilter->GetFilterName()); } CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf79972XLSX) diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx index 0d0c6655aaab..fc794b00ab60 100644 --- a/sc/source/filter/excel/excdoc.cxx +++ b/sc/source/filter/excel/excdoc.cxx @@ -852,13 +852,17 @@ void ExcDocument::WriteXml( XclExpXmlStream& rStrm ) FSNS(XML_xmlns, XML_xr6), rStrm.getNamespaceURL(OOX_NS(xr6)), FSNS(XML_xmlns, XML_xr10), rStrm.getNamespaceURL(OOX_NS(xr10)), FSNS(XML_xmlns, XML_xr2), rStrm.getNamespaceURL(OOX_NS(xr2)) ); - rWorkbook->singleElement( XML_fileVersion, - XML_appName, "Calc" + + rtl::Reference<sax_fastparser::FastAttributeList> pAttrListFileVersion + = sax_fastparser::FastSerializerHelper::createAttrList(); + pAttrListFileVersion->add(XML_appName, "Calc"); + std::optional<sal_Int16> oLow = GetExtDocOptions().GetDocSettings().moLowestEdited; + if (oLow.has_value()) + pAttrListFileVersion->add(XML_lowestEdited, OString::number(*oLow)); // OOXTODO: XML_codeName // OOXTODO: XML_lastEdited - // OOXTODO: XML_lowestEdited // OOXTODO: XML_rupBuild - ); + rWorkbook->singleElement(XML_fileVersion, pAttrListFileVersion); if (bHasPasswordHash) rWorkbook->singleElement(XML_fileSharing, diff --git a/sc/source/filter/inc/workbooksettings.hxx b/sc/source/filter/inc/workbooksettings.hxx index 1078b3e6c096..729daa754664 100644 --- a/sc/source/filter/inc/workbooksettings.hxx +++ b/sc/source/filter/inc/workbooksettings.hxx @@ -19,6 +19,7 @@ #pragma once +#include <scextopt.hxx> #include "workbookhelper.hxx" namespace oox { class AttributeList; } @@ -81,9 +82,12 @@ class WorkbookSettings : public WorkbookHelper { public: explicit WorkbookSettings( const WorkbookHelper& rHelper ); + ~WorkbookSettings(); /** Imports the fileSharing element containing write protection settings. */ void importFileSharing( const AttributeList& rAttribs ); + /** Imports the fileVersion element containing last-edited-by settings. */ + void importFileVersion( const AttributeList& rAttribs ); /** Imports the workbookPr element containing global workbook settings. */ void importWorkbookPr( const AttributeList& rAttribs ); /** Imports the calcPr element containing workbook calculation settings. */ @@ -112,6 +116,7 @@ private: FileSharingModel maFileSharing; WorkbookSettingsModel maBookSettings; CalcSettingsModel maCalcSettings; + ScExtDocOptions maExtDocOptions; }; } // namespace oox::xls diff --git a/sc/source/filter/oox/workbookfragment.cxx b/sc/source/filter/oox/workbookfragment.cxx index 18eed29be335..14db1335349d 100644 --- a/sc/source/filter/oox/workbookfragment.cxx +++ b/sc/source/filter/oox/workbookfragment.cxx @@ -118,6 +118,7 @@ ContextHandlerRef WorkbookFragment::onCreateContext( sal_Int32 nElement, const A case XLS_TOKEN( pivotCaches ): return this; case XLS_TOKEN( fileSharing ): getWorkbookSettings().importFileSharing( rAttribs ); break; + case XLS_TOKEN( fileVersion ): getWorkbookSettings().importFileVersion( rAttribs ); break; case XLS_TOKEN( workbookPr ): getWorkbookSettings().importWorkbookPr( rAttribs ); break; case XLS_TOKEN( calcPr ): getWorkbookSettings().importCalcPr( rAttribs ); break; case XLS_TOKEN( oleSize ): getViewSettings().importOleSize( rAttribs ); break; diff --git a/sc/source/filter/oox/workbooksettings.cxx b/sc/source/filter/oox/workbooksettings.cxx index 0914934a344a..1bea1995d81f 100644 --- a/sc/source/filter/oox/workbooksettings.cxx +++ b/sc/source/filter/oox/workbooksettings.cxx @@ -38,7 +38,9 @@ #include <oox/token/tokens.hxx> #include <unitconverter.hxx> #include <biffhelper.hxx> +#include <document.hxx> #include <docuno.hxx> +#include <scextopt.hxx> namespace oox::xls { @@ -107,6 +109,13 @@ CalcSettingsModel::CalcSettingsModel() : WorkbookSettings::WorkbookSettings( const WorkbookHelper& rHelper ) : WorkbookHelper( rHelper ) { + if (getScDocument().GetExtDocOptions()) + maExtDocOptions = *getScDocument().GetExtDocOptions(); +} + +WorkbookSettings::~WorkbookSettings() +{ + getScDocument().SetExtDocOptions(std::make_unique<ScExtDocOptions>(maExtDocOptions)); } void WorkbookSettings::importFileSharing( const AttributeList& rAttribs ) @@ -120,6 +129,13 @@ void WorkbookSettings::importFileSharing( const AttributeList& rAttribs ) maFileSharing.mbRecommendReadOnly = rAttribs.getBool( XML_readOnlyRecommended, false ); } +void WorkbookSettings::importFileVersion(const AttributeList& rAttribs) +{ + const sal_Int16 nLow = rAttribs.getInteger(XML_lowestEdited, -1); + if (nLow != -1) + maExtDocOptions.GetDocSettings().moLowestEdited = nLow; +} + void WorkbookSettings::importWorkbookPr( const AttributeList& rAttribs ) { maBookSettings.maCodeName = rAttribs.getString( XML_codeName, OUString() );
