dev/null |binary sc/qa/unit/data/xlsx/enhancedProtectionRangeShorthand.xlsx |binary sc/qa/unit/subsequent_export_test2.cxx | 9 -- sc/source/core/tool/rangelst.cxx | 4 sc/source/filter/excel/xestream.cxx | 3 sc/source/filter/inc/addressconverter.hxx | 25 ----- sc/source/filter/oox/addressconverter.cxx | 58 +++---------- sc/source/filter/oox/autofilterbuffer.cxx | 6 - sc/source/filter/oox/commentsbuffer.cxx | 4 sc/source/filter/oox/pivotcachebuffer.cxx | 4 sc/source/filter/oox/pivottablebuffer.cxx | 4 sc/source/filter/oox/tablebuffer.cxx | 4 sc/source/filter/oox/worksheetfragment.cxx | 3 13 files changed, 38 insertions(+), 86 deletions(-)
New commits: commit c051445a23fd9f5456eae5445c4cd5d30f02bd9d Author: Justin Luth <justin.l...@collabora.com> AuthorDate: Tue Apr 23 20:15:27 2024 -0400 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Mon Feb 17 11:32:11 2025 +0100 xlsx import: parse short-hand version of range address In order to indicate the entire row or column (for the benefit of a spreadsheet program that may eventually increase the supported number of rows or columns it supports) a short-handed form of the range can be used like "B:B" for the entire column, or "1:10" for the first 10 rows of all columns. Shorthand is nicer for humans and allows for future expansion of row/column space This reverts 24.8 commit a29d91ac403f1ed431ca95b8b9c290bd354c3ae7 xlsx export: never export short-hand version of range address which reduced the likelihood that we will ever see such a range, because Excel seems to always write out the full address. However, the shorthand version is definitely valid for Excel. Earlier patchsets checked that the new method and the old method returned the same values for all existing unit tests. Change-Id: I7677dfc207771a32222095416f728f7cd34a4f75 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166570 Reviewed-by: Justin Luth <jl...@mail.com> Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins (cherry picked from commit cfb913db1b2024f8ff6a55f45742b303107a1924) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181766 Reviewed-by: Andras Timar <andras.ti...@collabora.com> Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/sc/qa/unit/data/xlsx/enhancedProtectionRangeShorthand.xlsx b/sc/qa/unit/data/xlsx/enhancedProtectionRangeShorthand.xlsx new file mode 100644 index 000000000000..1f8a5675e796 Binary files /dev/null and b/sc/qa/unit/data/xlsx/enhancedProtectionRangeShorthand.xlsx differ diff --git a/sc/qa/unit/data/xlsx/protectedRange.xlsx b/sc/qa/unit/data/xlsx/protectedRange.xlsx deleted file mode 100644 index a185ac3598d0..000000000000 Binary files a/sc/qa/unit/data/xlsx/protectedRange.xlsx and /dev/null differ diff --git a/sc/qa/unit/subsequent_export_test2.cxx b/sc/qa/unit/subsequent_export_test2.cxx index 0252c8e6321e..55908afd7558 100644 --- a/sc/qa/unit/subsequent_export_test2.cxx +++ b/sc/qa/unit/subsequent_export_test2.cxx @@ -1324,15 +1324,14 @@ CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf126024XLSX) "External"); } -CPPUNIT_TEST_FIXTURE(ScExportTest2, testProtectedRange) +CPPUNIT_TEST_FIXTURE(ScExportTest2, testEnhancedProtectionRangeShorthand) { - createScDoc("xlsx/protectedRange.xlsx"); + createScDoc("xlsx/enhancedProtectionRangeShorthand.xlsx"); save("Calc Office Open XML"); - xmlDocUniquePtr pDoc = parseExport("xl/worksheets/sheet1.xml"); CPPUNIT_ASSERT(pDoc); - // entire row was being exported as shorthand 'B:B' which LO couldn't read back - assertXPath(pDoc, "//x:protectedRanges/x:protectedRange"_ostr, "sqref"_ostr, "B1:B1048576"); + // the shorthand version was not recognized as a valid range on import + assertXPath(pDoc, "//x:protectedRanges/x:protectedRange[1]"_ostr, "sqref"_ostr, "C:C"); } CPPUNIT_TEST_FIXTURE(ScExportTest2, testTdf91332) diff --git a/sc/source/core/tool/rangelst.cxx b/sc/source/core/tool/rangelst.cxx index 998187b42996..f84c92c7a7ae 100644 --- a/sc/source/core/tool/rangelst.cxx +++ b/sc/source/core/tool/rangelst.cxx @@ -133,10 +133,6 @@ void ScRangeList::Format( OUString& rStr, ScRefFlags nFlags, const ScDocument& r formula::FormulaGrammar::AddressConvention eConv, sal_Unicode cDelimiter, bool bFullAddressNotation ) const { - // LO's AddressConverter::parseOoxAddress2d cannot import a short-hand address, - // so definitely do not export that way. - assert(eConv != FormulaGrammar::CONV_XL_OOX || bFullAddressNotation); - if (!cDelimiter) cDelimiter = ScCompiler::GetNativeSymbolChar(ocSep); diff --git a/sc/source/filter/excel/xestream.cxx b/sc/source/filter/excel/xestream.cxx index 443326462af3..d54e193c32cc 100644 --- a/sc/source/filter/excel/xestream.cxx +++ b/sc/source/filter/excel/xestream.cxx @@ -746,8 +746,7 @@ OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRange& rRange, b OString XclXmlUtils::ToOString( const ScDocument& rDoc, const ScRangeList& rRangeList ) { OUString s; - rRangeList.Format(s, ScRefFlags::VALID, rDoc, FormulaGrammar::CONV_XL_OOX, ' ', - /*FullAddressNotation=*/true); + rRangeList.Format(s, ScRefFlags::VALID, rDoc, FormulaGrammar::CONV_XL_OOX, ' '); return s.toUtf8(); } diff --git a/sc/source/filter/inc/addressconverter.hxx b/sc/source/filter/inc/addressconverter.hxx index 19c1b001be0d..034bcbacaf39 100644 --- a/sc/source/filter/inc/addressconverter.hxx +++ b/sc/source/filter/inc/addressconverter.hxx @@ -125,29 +125,6 @@ public: static bool parseOoxAddress2d( sal_Int32& ornColumn, sal_Int32& ornRow, std::string_view pStr ); - /** Tries to parse the passed string for a 2d cell range in A1 notation. - - This function accepts all strings that match the regular expression - "ADDR(:ADDR)?" (without quotes), where ADDR is a cell address accepted - by the parseOoxAddress2d() function of this class. It is up to the - caller to handle cell ranges outside of a specific valid range (e.g. - the entire spreadsheet). - - @param ornStartColumn (out-parameter) Returns the converted start column index. - @param ornStartRow (out-parameter) returns the converted start row index. - @param ornEndColumn (out-parameter) Returns the converted end column index. - @param ornEndRow (out-parameter) returns the converted end row index. - @param rString The string containing the cell address. - @param nStart Start index of string part in rString to be parsed. - - @return true = Parsed string was valid, returned values can be used. - */ - static bool parseOoxRange2d( - sal_Int32& ornStartColumn, sal_Int32& ornStartRow, - sal_Int32& ornEndColumn, sal_Int32& ornEndRow, - std::u16string_view aString, - sal_Int32 nStart = 0 ); - /** Returns the biggest valid cell address in the own Calc document. */ const ScAddress& getMaxApiAddress() const { return maMaxApiPos; } @@ -346,7 +323,7 @@ public: static bool convertToCellRangeUnchecked( ScRange& orRange, std::u16string_view aString, - sal_Int16 nSheet ); + sal_Int16 nSheet, const ScDocument& rDoc); /** Tries to convert the passed string to a cell range address. diff --git a/sc/source/filter/oox/addressconverter.cxx b/sc/source/filter/oox/addressconverter.cxx index 2408e2670ee8..4109ef3c8833 100644 --- a/sc/source/filter/oox/addressconverter.cxx +++ b/sc/source/filter/oox/addressconverter.cxx @@ -28,6 +28,7 @@ #include <oox/core/filterbase.hxx> #include <oox/helper/binaryinputstream.hxx> #include <docuno.hxx> +#include <rangeutl.hxx> namespace oox::xls { @@ -196,34 +197,6 @@ bool AddressConverter::parseOoxAddress2d( sal_Int32& ornColumn, sal_Int32& ornRo return (ornColumn >= 0) && (ornRow >= 0); } -bool AddressConverter::parseOoxRange2d( - sal_Int32& ornStartColumn, sal_Int32& ornStartRow, - sal_Int32& ornEndColumn, sal_Int32& ornEndRow, - std::u16string_view aString, sal_Int32 nStart ) -{ - ornStartColumn = ornStartRow = ornEndColumn = ornEndRow = 0; - if( (nStart < 0) || (nStart >= sal_Int32(aString.size())) ) - return false; - - sal_Int32 nEnd = nStart + ( aString.size() - nStart ); - size_t nColonPos = aString.find( ':', nStart ); - if( nColonPos != std::u16string_view::npos && (nStart < sal_Int32(nColonPos)) && (sal_Int32(nColonPos + 1) < nEnd) ) - { - return - parseOoxAddress2d( ornStartColumn, ornStartRow, aString, nStart, nColonPos - nStart ) && - parseOoxAddress2d( ornEndColumn, ornEndRow, aString, nColonPos + 1, SAL_MAX_INT32 - nColonPos - 1 ); - } - - if( parseOoxAddress2d( ornStartColumn, ornStartRow, aString, nStart ) ) - { - ornEndColumn = ornStartColumn; - ornEndRow = ornStartRow; - return true; - } - - return false; -} - bool AddressConverter::checkCol( sal_Int32 nCol, bool bTrackOverflow ) { bool bValid = (0 <= nCol) && ( nCol <= maMaxPos.Col() ); @@ -374,28 +347,27 @@ bool AddressConverter::validateCellRange( ScRange& orRange, bool bAllowOverflow, return true; } -bool AddressConverter::convertToCellRangeUnchecked( ScRange& orRange, - std::u16string_view aString, sal_Int16 nSheet ) +bool AddressConverter::convertToCellRangeUnchecked(ScRange& orRange, std::u16string_view aString, + sal_Int16 nSheet, const ScDocument& rDoc) { - orRange.aStart.SetTab( nSheet ); - orRange.aEnd.SetTab( nSheet ); - sal_Int32 aStartCol = orRange.aStart.Col(); - sal_Int32 aStartRow = orRange.aStart.Row(); - sal_Int32 aEndCol = orRange.aEnd.Col(); - sal_Int32 aEndRow = orRange.aEnd.Row(); - bool bReturnValue = parseOoxRange2d( aStartCol, aStartRow, aEndCol, aEndRow, aString ); - orRange.aStart.SetCol( aStartCol ); - orRange.aStart.SetRow( aStartRow ); - orRange.aEnd.SetCol( aEndCol ); - orRange.aEnd.SetRow( aEndRow ); - return bReturnValue; + orRange.aStart.SetTab(nSheet); + orRange.aEnd.SetTab(nSheet); + + if (aString.empty()) + return false; + + sal_Int32 nOffset = 0; + bool bRet = ScRangeStringConverter::GetRangeFromString( + orRange, aString, rDoc, formula::FormulaGrammar::CONV_XL_OOX, nOffset); + + return bRet && nOffset > 0 && o3tl::make_unsigned(nOffset) == aString.length(); } bool AddressConverter::convertToCellRange( ScRange& orRange, std::u16string_view aString, sal_Int16 nSheet, bool bAllowOverflow, bool bTrackOverflow ) { return - convertToCellRangeUnchecked( orRange, aString, nSheet ) && + convertToCellRangeUnchecked(orRange, aString, nSheet, getScDocument()) && validateCellRange( orRange, bAllowOverflow, bTrackOverflow ); } diff --git a/sc/source/filter/oox/autofilterbuffer.cxx b/sc/source/filter/oox/autofilterbuffer.cxx index 95ffb0faaf68..756c870c99f2 100644 --- a/sc/source/filter/oox/autofilterbuffer.cxx +++ b/sc/source/filter/oox/autofilterbuffer.cxx @@ -674,7 +674,7 @@ SortCondition::SortCondition( const WorkbookHelper& rHelper ) : void SortCondition::importSortCondition( const AttributeList& rAttribs, sal_Int16 nSheet ) { OUString aRangeStr = rAttribs.getString( XML_ref, OUString() ); - AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet ); + AddressConverter::convertToCellRangeUnchecked(maRange, aRangeStr, nSheet, getScDocument()); maSortCustomList = rAttribs.getString( XML_customList, OUString() ); mbDescending = rAttribs.getBool( XML_descending, false ); @@ -690,7 +690,7 @@ AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) : void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet ) { OUString aRangeStr = rAttribs.getString( XML_ref, OUString() ); - AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet ); + AddressConverter::convertToCellRangeUnchecked(maRange, aRangeStr, nSheet, getScDocument()); } void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet ) @@ -703,7 +703,7 @@ void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet void AutoFilter::importSortState( const AttributeList& rAttribs, sal_Int16 nSheet ) { OUString aRangeStr = rAttribs.getString( XML_ref, OUString() ); - AddressConverter::convertToCellRangeUnchecked( maSortRange, aRangeStr, nSheet ); + AddressConverter::convertToCellRangeUnchecked(maSortRange, aRangeStr, nSheet, getScDocument()); } FilterColumn& AutoFilter::createFilterColumn() diff --git a/sc/source/filter/oox/commentsbuffer.cxx b/sc/source/filter/oox/commentsbuffer.cxx index adbac7c92461..abca06723a92 100644 --- a/sc/source/filter/oox/commentsbuffer.cxx +++ b/sc/source/filter/oox/commentsbuffer.cxx @@ -118,7 +118,9 @@ void Comment::importComment( const AttributeList& rAttribs ) { maModel.mnAuthorId = rAttribs.getInteger( XML_authorId, -1 ); // cell range will be checked while inserting the comment into the document - AddressConverter::convertToCellRangeUnchecked( maModel.maRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex() ); + AddressConverter::convertToCellRangeUnchecked(maModel.maRange, + rAttribs.getString(XML_ref, OUString()), + getSheetIndex(), getScDocument()); } void Comment::importCommentPr( const AttributeList& rAttribs ) diff --git a/sc/source/filter/oox/pivotcachebuffer.cxx b/sc/source/filter/oox/pivotcachebuffer.cxx index d8cf350bf9a5..6f0a13bcd561 100644 --- a/sc/source/filter/oox/pivotcachebuffer.cxx +++ b/sc/source/filter/oox/pivotcachebuffer.cxx @@ -922,7 +922,9 @@ void PivotCache::importWorksheetSource( const AttributeList& rAttribs, const Rel // resolve URL of external document maTargetUrl = rRelations.getExternalTargetFromRelId( maSheetSrcModel.maRelId ); // store range address unchecked with sheet index 0, will be resolved/checked later - AddressConverter::convertToCellRangeUnchecked( maSheetSrcModel.maRange, rAttribs.getString( XML_ref, OUString() ), 0 ); + AddressConverter::convertToCellRangeUnchecked(maSheetSrcModel.maRange, + rAttribs.getString(XML_ref, OUString()), 0, + getScDocument()); } void PivotCache::importPCDefinition( SequenceInputStream& rStrm ) diff --git a/sc/source/filter/oox/pivottablebuffer.cxx b/sc/source/filter/oox/pivottablebuffer.cxx index 0d8025cbf8f5..e51d7979a494 100644 --- a/sc/source/filter/oox/pivottablebuffer.cxx +++ b/sc/source/filter/oox/pivottablebuffer.cxx @@ -1037,7 +1037,9 @@ void PivotTable::importPivotTableDefinition( const AttributeList& rAttribs ) void PivotTable::importLocation( const AttributeList& rAttribs, sal_Int16 nSheet ) { - AddressConverter::convertToCellRangeUnchecked( maLocationModel.maRange, rAttribs.getString( XML_ref, OUString() ), nSheet ); + AddressConverter::convertToCellRangeUnchecked(maLocationModel.maRange, + rAttribs.getString(XML_ref, OUString()), nSheet, + getScDocument()); maLocationModel.mnFirstHeaderRow = rAttribs.getInteger( XML_firstHeaderRow, 0 ); maLocationModel.mnFirstDataRow = rAttribs.getInteger( XML_firstDataRow, 0 ); maLocationModel.mnFirstDataCol = rAttribs.getInteger( XML_firstDataCol, 0 ); diff --git a/sc/source/filter/oox/tablebuffer.cxx b/sc/source/filter/oox/tablebuffer.cxx index dec0498f4678..d43f32806a8c 100644 --- a/sc/source/filter/oox/tablebuffer.cxx +++ b/sc/source/filter/oox/tablebuffer.cxx @@ -57,7 +57,9 @@ Table::Table( const WorkbookHelper& rHelper ) : void Table::importTable( const AttributeList& rAttribs, sal_Int16 nSheet ) { - AddressConverter::convertToCellRangeUnchecked( maModel.maRange, rAttribs.getString( XML_ref, OUString() ), nSheet ); + AddressConverter::convertToCellRangeUnchecked(maModel.maRange, + rAttribs.getString(XML_ref, OUString()), nSheet, + getScDocument()); maModel.maProgName = rAttribs.getXString( XML_name, OUString() ); maModel.maDisplayName = rAttribs.getXString( XML_displayName, OUString() ); maModel.mnId = rAttribs.getInteger( XML_id, -1 ); diff --git a/sc/source/filter/oox/worksheetfragment.cxx b/sc/source/filter/oox/worksheetfragment.cxx index 30156058e7b0..0b31247bfdae 100644 --- a/sc/source/filter/oox/worksheetfragment.cxx +++ b/sc/source/filter/oox/worksheetfragment.cxx @@ -643,7 +643,8 @@ void WorksheetFragment::importPageSetUpPr( const AttributeList& rAttribs ) void WorksheetFragment::importDimension( const AttributeList& rAttribs ) { ScRange aRange; - AddressConverter::convertToCellRangeUnchecked( aRange, rAttribs.getString( XML_ref, OUString() ), getSheetIndex() ); + AddressConverter::convertToCellRangeUnchecked(aRange, rAttribs.getString(XML_ref, OUString()), + getSheetIndex(), getScDocument()); /* OOXML stores the used area, if existing, or "A1" if the sheet is empty. In case of "A1", the dimension at the WorksheetHelper object will not be set. If the cell A1 exists, the used area will be updated while