diff --git a/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs b/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs index ca2e345b..1531b60d 100644 --- a/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs @@ -110,7 +110,7 @@ public void TestOpenOffice() } /// - /// Issue 11 - OpenOffice files were skipping the first row if IsFirstRowAsColumnNames = false; + /// Issue 11 - OpenOffice files were skipping the first row if IsFirstRowAsColumnNames = false. /// [Test] public void GitIssue11OpenOfficeRowCount() @@ -170,7 +170,7 @@ public void Issue11572CodePage() } /// - /// Not fixed yet + /// Not fixed yet. /// [Test] public void Issue11545NoIndex() @@ -211,7 +211,7 @@ public void Issue11636BiffStream() /// Not fixed yet /// The problem occurs with unseekable stream and logic related to minifat that uses seek /// It should probably only use seek if it needs to go backwards, I think at the moment it uses seek all the time - /// which is probably not good for performance + /// which is probably not good for performance. /// [Test] [Ignore("Not fixed yet")] @@ -228,7 +228,7 @@ public void Issue1163911644ForwardOnlyStream() /// Not fixed yet /// The problem occurs with unseekable stream and logic related to minifat that uses seek /// It should probably only use seek if it needs to go backwards, I think at the moment it uses seek all the time - /// which is probably not good for performance + /// which is probably not good for performance. /// [Test] public void Issue12556Corrupt() @@ -243,7 +243,7 @@ public void Issue12556Corrupt() } /// - /// Some spreadsheets were crashing with index out of range error (from SSRS) + /// Some spreadsheets were crashing with index out of range error (from SSRS). /// [Test] public void TestIssue11818OutOfRange() @@ -1069,6 +1069,30 @@ public void GitIssue578() Assert.That(values3, Is.EqualTo(new object[] { 1, 2, 3, 4 })); } + [Test] + public void GitIssue642_ActiveSheet() + { + using var reader = OpenReader("Test_git_issue_642"); + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + FilterSheet = (tableReader, sheetIndex) => tableReader.IsActiveSheet + }); + Assert.That(reader.ActiveSheet, Is.EqualTo(5)); + Assert.That(dataSet.Tables[0].TableName, Is.EqualTo("List6")); + } + + [Test] + public void GitIssue642_ActiveSheet_SingleWorksheet() + { + using var reader = OpenReader("Test_git_issue_642onesheet"); + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + FilterSheet = (tableReader, sheetIndex) => tableReader.IsActiveSheet + }); + Assert.That(reader.ActiveSheet, Is.EqualTo(0)); + Assert.That(dataSet.Tables[0].TableName, Is.EqualTo("List1")); + } + protected override IExcelDataReader OpenReader(Stream stream, ExcelReaderConfiguration configuration = null) { return ExcelReaderFactory.CreateBinaryReader(stream, configuration); diff --git a/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs b/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs index e9a52c00..b8e0b10b 100644 --- a/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs @@ -3,7 +3,7 @@ namespace ExcelDataReader.Tests; /// -/// Most CSV test data came from csv-spectrum: https://github.com/maxogden/csv-spectrum +/// Most CSV test data came from csv-spectrum: https://github.com/maxogden/csv-spectrum. /// public class ExcelCsvReaderTest { @@ -467,4 +467,16 @@ public void GitIssue463() Assert.That(row4, Is.EqualTo(new object[] { "Test3", "IJK", "ZZ", "10,143.27", "0.00" })); }); } + + [Test] + public void GitIssue642_ActiveSheet() + { + using var reader = ExcelReaderFactory.CreateCsvReader(Configuration.GetTestWorkbook("csv\\MOCK_DATA.csv")); + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + FilterSheet = (tableReader, sheetIndex) => tableReader.IsActiveSheet + }); + Assert.That(reader.ActiveSheet, Is.EqualTo(0)); + Assert.That(dataSet.Tables.Count, Is.EqualTo(1)); + } } diff --git a/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs b/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs index bbe3bb24..06e15562 100644 --- a/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs @@ -14,6 +14,30 @@ public void GitIssue635() Assert.That(dataSet.Tables[0].Rows[0].ItemArray, Is.EqualTo(new[] { "A", "B", "C", "D", "E", "F" })); } + [Test] + public void GitIssue642_ActiveSheet() + { + using var reader = OpenReader("Test_git_issue_642"); + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + FilterSheet = (tableReader, sheetIndex) => tableReader.IsActiveSheet + }); + Assert.That(reader.ActiveSheet, Is.EqualTo(5)); + Assert.That(dataSet.Tables[0].TableName, Is.EqualTo("List6")); + } + + [Test] + public void GitIssue642_ActiveSheet_SingleWorksheet() + { + using var reader = OpenReader("Test_git_issue_642onesheet"); + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + FilterSheet = (tableReader, sheetIndex) => tableReader.IsActiveSheet + }); + Assert.That(reader.ActiveSheet, Is.EqualTo(0)); + Assert.That(dataSet.Tables[0].TableName, Is.EqualTo("List1")); + } + /// protected override Stream OpenStream(string name) { diff --git a/src/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs b/src/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs index 99f37384..93ee0df9 100644 --- a/src/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelOpenXmlReaderTest.cs @@ -458,6 +458,30 @@ public void GitIssue518MultipleHeaderRows() } } + [Test] + public void GitIssue642_ActiveSheet() + { + using var reader = OpenReader("Test_git_issue_642"); + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + FilterSheet = (tableReader, sheetIndex) => tableReader.IsActiveSheet + }); + Assert.That(reader.ActiveSheet, Is.EqualTo(5)); + Assert.That(dataSet.Tables[0].TableName, Is.EqualTo("List6")); + } + + [Test] + public void GitIssue642_ActiveSheet_SingleWorksheet() + { + using var reader = OpenReader("Test_git_issue_642onesheet"); + var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() + { + FilterSheet = (tableReader, sheetIndex) => tableReader.IsActiveSheet + }); + Assert.That(reader.ActiveSheet, Is.EqualTo(0)); + Assert.That(dataSet.Tables[0].TableName, Is.EqualTo("List1")); + } + protected override IExcelDataReader OpenReader(Stream stream, ExcelReaderConfiguration configuration = null) => ExcelReaderFactory.CreateOpenXmlReader(stream, configuration); diff --git a/src/ExcelDataReader.Tests/ExcelTestBase.cs b/src/ExcelDataReader.Tests/ExcelTestBase.cs index 30671310..0a12df18 100644 --- a/src/ExcelDataReader.Tests/ExcelTestBase.cs +++ b/src/ExcelDataReader.Tests/ExcelTestBase.cs @@ -49,7 +49,7 @@ public void Issue11773ExponentialCommas() } /// - /// Makes sure that we can read data from the first row of last sheet + /// Makes sure that we can read data from the first row of last sheet. /// [Test] public void Issue12271NextResultSet() diff --git a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs index 9fb5b70d..8e578bdc 100644 --- a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs +++ b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs @@ -77,6 +77,8 @@ internal XlsWorkbook(Stream stream, string password, Encoding fallbackEncoding) public int ResultsCount => Sheets?.Count ?? -1; + public int ActiveSheet { get; private set; } + public static bool IsRawBiffStream(byte[] bytes) { if (bytes.Length < 8) @@ -203,6 +205,10 @@ private void ReadWorkbookGlobals(XlsBiffStream biffStream) ExtSST = rec; break; + case XlsBiffRecord _ when rec.Id == BIFFRECORDTYPE.WINDOW1: + ActiveSheet = rec.ReadInt16(10); + break; + // case BIFFRECORDTYPE.PROTECT: // case BIFFRECORDTYPE.PROT4REVPASSWORD: // IsProtected diff --git a/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs b/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs index 38fe95bc..c3239d2f 100644 --- a/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs +++ b/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs @@ -42,6 +42,11 @@ public XlsWorksheet(XlsWorkbook workbook, XlsBiffBoundSheet refSheet, Stream str /// public string VisibleState { get; } + /// + /// Gets a value indicating whether the sheet is active. + /// + public bool IsActiveSheet { get; } + public HeaderFooter HeaderFooter { get; private set; } public CellRange[] MergeCells { get; private set; } diff --git a/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs b/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs index aeb24a95..f1af684d 100644 --- a/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs +++ b/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs @@ -7,6 +7,8 @@ internal sealed class CsvWorkbook(Stream stream, Encoding encoding, char[] autod { public int ResultsCount => 1; + public int ActiveSheet => 0; + public Stream Stream { get; } = stream; public Encoding Encoding { get; } = encoding; diff --git a/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs b/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs index a73be669..65141453 100644 --- a/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs +++ b/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs @@ -34,6 +34,10 @@ public CsvWorksheet(Stream stream, Encoding fallbackEncoding, char[] autodetectS } } + public static int ActiveSheet => 0; + + public static bool IsActiveSheet => true; + public string Name => string.Empty; public string CodeName => null; diff --git a/src/ExcelDataReader/Core/IWorkbook.cs b/src/ExcelDataReader/Core/IWorkbook.cs index 251a213d..1ed4f34b 100644 --- a/src/ExcelDataReader/Core/IWorkbook.cs +++ b/src/ExcelDataReader/Core/IWorkbook.cs @@ -11,6 +11,8 @@ internal interface IWorkbook { int ResultsCount { get; } + int ActiveSheet { get; } + IEnumerable ReadWorksheets(); NumberFormatString GetNumberFormatString(int index); diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs index eea1153d..857f82a1 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs @@ -8,7 +8,8 @@ internal sealed class BiffWorkbookReader(Stream stream, Dictionary _worksheetRels = worksheetRels; private enum SheetVisibility : byte @@ -42,6 +43,11 @@ protected override Record ReadOverride(byte[] buffer, uint recordId, uint record string name = GetString(buffer, offset + 4, nameLength); return new SheetRecord(name, id, rid, state, rid != null && _worksheetRels.TryGetValue(rid, out var path) ? path : null); + + case BrtBookView: + int activeSheet = (int)GetDWord(buffer, 24); + return new WorkbookActRecord(activeSheet); + default: return Record.Default; } diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs b/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs new file mode 100644 index 00000000..f5723504 --- /dev/null +++ b/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ExcelDataReader.Core.OpenXmlFormat.Records; + +internal sealed class WorkbookActRecord : Record +{ + public WorkbookActRecord(int activeSheet) + { + this.ActiveSheet = activeSheet; + } + + public int ActiveSheet { get; } +} diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs index 57ae651b..a96f5072 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs @@ -22,6 +22,8 @@ public XlsxWorkbook(ZipWorker zipWorker) public int ResultsCount => Sheets?.Count ?? -1; + public int ActiveSheet { get; private set; } + public IEnumerable ReadWorksheets() { foreach (var sheet in Sheets) @@ -45,6 +47,9 @@ private void ReadWorkbook() case SheetRecord sheet: Sheets.Add(sheet); break; + case WorkbookActRecord activeSheet: + ActiveSheet = activeSheet.ActiveSheet; + break; } } } diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs index 35455f69..dc794872 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs @@ -91,6 +91,8 @@ public XlsxWorksheet(ZipWorker document, XlsxWorkbook workbook, SheetRecord refS public string VisibleState { get; } + public bool IsActiveSheet { get; } + public HeaderFooter HeaderFooter { get; } public double DefaultRowHeight { get; } diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs index 308b41cd..c10a4ff4 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs @@ -66,6 +66,28 @@ protected override IEnumerable ReadOverride() } } } + else if (Reader.IsStartElement("bookViews", ProperNamespaces.NsSpreadsheetMl)) + { + if (!XmlReaderHelper.ReadFirstContent(Reader)) + { + continue; + } + + while (!Reader.EOF) + { + if (Reader.IsStartElement("workbookView", ProperNamespaces.NsSpreadsheetMl)) + { + string activeTab = Reader.GetAttribute("activeTab"); + int activeTabInt = int.TryParse(activeTab, out var result) ? result : 0; + yield return new WorkbookActRecord(activeTabInt); + Reader.Skip(); + } + else if (!XmlReaderHelper.SkipContent(Reader)) + { + break; + } + } + } else if (!XmlReaderHelper.SkipContent(Reader)) { yield break; diff --git a/src/ExcelDataReader/ExcelDataReader.cs b/src/ExcelDataReader/ExcelDataReader.cs index 8c0ffdc9..c489ada1 100644 --- a/src/ExcelDataReader/ExcelDataReader.cs +++ b/src/ExcelDataReader/ExcelDataReader.cs @@ -16,6 +16,7 @@ internal abstract class ExcelDataReader : IExcelDataReade private IEnumerator _rowIterator; private IEnumerator _cachedWorksheetIterator; private List _cachedWorksheets; + private int _idx; ~ExcelDataReader() { @@ -28,6 +29,10 @@ internal abstract class ExcelDataReader : IExcelDataReade public string VisibleState => _worksheetIterator?.Current?.VisibleState; + public int ActiveSheet => this.Workbook.ActiveSheet; + + public bool IsActiveSheet => _idx == this.Workbook.ActiveSheet; + public HeaderFooter HeaderFooter => _worksheetIterator?.Current?.HeaderFooter; // We shouldn't expose the internal array here. @@ -213,11 +218,13 @@ public void Reset() _worksheetIterator = null; _rowIterator = null; + _idx = 0; + ResetSheetData(); if (Workbook != null) { - _worksheetIterator = ReadWorksheetsWithCache().GetEnumerator(); // Workbook.ReadWorksheets().GetEnumerator(); + _worksheetIterator = ReadWorksheetsWithCache().GetEnumerator(); if (!_worksheetIterator.MoveNext()) { _worksheetIterator.Dispose(); @@ -263,6 +270,9 @@ public bool NextResult() } _rowIterator = _worksheetIterator.Current.ReadRows().GetEnumerator(); + + _idx++; + return true; } diff --git a/src/ExcelDataReader/IExcelDataReader.cs b/src/ExcelDataReader/IExcelDataReader.cs index f14a4ff1..13f48a3b 100644 --- a/src/ExcelDataReader/IExcelDataReader.cs +++ b/src/ExcelDataReader/IExcelDataReader.cs @@ -22,6 +22,16 @@ public interface IExcelDataReader : IDataReader /// string VisibleState { get; } + /// + /// Gets the active sheet. + /// + int ActiveSheet { get; } + + /// + /// Gets a value indicating whether the worksheet is active. + /// + bool IsActiveSheet { get; } + /// /// Gets the sheet header and footer -or- if none set. /// diff --git a/src/TestData/Test_git_issue_642.xls b/src/TestData/Test_git_issue_642.xls new file mode 100644 index 00000000..085d9e74 Binary files /dev/null and b/src/TestData/Test_git_issue_642.xls differ diff --git a/src/TestData/Test_git_issue_642.xlsb b/src/TestData/Test_git_issue_642.xlsb new file mode 100644 index 00000000..b16f6fbb Binary files /dev/null and b/src/TestData/Test_git_issue_642.xlsb differ diff --git a/src/TestData/Test_git_issue_642.xlsx b/src/TestData/Test_git_issue_642.xlsx new file mode 100644 index 00000000..8a38c4f5 Binary files /dev/null and b/src/TestData/Test_git_issue_642.xlsx differ diff --git a/src/TestData/Test_git_issue_642onesheet.xls b/src/TestData/Test_git_issue_642onesheet.xls new file mode 100644 index 00000000..92295fc2 Binary files /dev/null and b/src/TestData/Test_git_issue_642onesheet.xls differ diff --git a/src/TestData/Test_git_issue_642onesheet.xlsb b/src/TestData/Test_git_issue_642onesheet.xlsb new file mode 100644 index 00000000..7793cd8e Binary files /dev/null and b/src/TestData/Test_git_issue_642onesheet.xlsb differ diff --git a/src/TestData/Test_git_issue_642onesheet.xlsx b/src/TestData/Test_git_issue_642onesheet.xlsx new file mode 100644 index 00000000..a7f08112 Binary files /dev/null and b/src/TestData/Test_git_issue_642onesheet.xlsx differ