From 24b5552102485f5b1226ab4ba1191473da0679cc Mon Sep 17 00:00:00 2001 From: asheinkin Date: Sun, 30 Jul 2023 11:35:39 +0300 Subject: [PATCH 1/8] Activesheet support added --- .../Core/BinaryFormat/XlsWorkbook.cs | 28 +++++---- .../Core/BinaryFormat/XlsWorksheet.cs | 7 ++- .../Core/CsvFormat/CsvWorkbook.cs | 4 +- .../Core/CsvFormat/CsvWorksheet.cs | 6 +- src/ExcelDataReader/Core/IWorkbook.cs | 5 +- .../BinaryFormat/BiffWorkbookReader.cs | 30 +++++---- .../Records/WorkbookActRecord.cs | 18 ++++++ .../Core/OpenXmlFormat/XlsxWorkbook.cs | 48 +++++++------- .../Core/OpenXmlFormat/XlsxWorksheet.cs | 4 +- .../XmlFormat/XmlWorkbookReader.cs | 59 +++++++++++++----- src/ExcelDataReader/ExcelDataReader.cs | 21 +++++-- src/ExcelDataReader/IExcelDataReader.cs | 18 ++++-- src/TestData/Test10x10.xls | Bin 13824 -> 13824 bytes src/TestData/TestOpen.xls | Bin 39424 -> 39424 bytes src/TestData/TestUnicodeChars.xls | Bin 14336 -> 14336 bytes src/TestData/Test_Excel_Dataset.xls | Bin 34816 -> 34816 bytes src/TestData/Test_Issue_11570_Excel2013.xls | Bin 25600 -> 25600 bytes src/TestData/Test_OpenOffice.xls | Bin 12800 -> 12800 bytes src/TestData/Test_git_issue_45.xls | Bin 42496 -> 42496 bytes src/TestData/Test_git_issue_642.xls | Bin 0 -> 37888 bytes src/TestData/Test_git_issue_642.xlsb | Bin 0 -> 12873 bytes src/TestData/Test_git_issue_642.xlsx | Bin 0 -> 11981 bytes src/TestData/Test_git_issue_642onesheet.xls | Bin 0 -> 26112 bytes src/TestData/Test_git_issue_642onesheet.xlsb | Bin 0 -> 7347 bytes src/TestData/Test_git_issue_642onesheet.xlsx | Bin 0 -> 7716 bytes src/TestData/roo_1900_base.xls | Bin 6656 -> 6656 bytes src/TestData/roo_1904_base.xls | Bin 19456 -> 19456 bytes 27 files changed, 174 insertions(+), 74 deletions(-) create mode 100644 src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs create mode 100644 src/TestData/Test_git_issue_642.xls create mode 100644 src/TestData/Test_git_issue_642.xlsb create mode 100644 src/TestData/Test_git_issue_642.xlsx create mode 100644 src/TestData/Test_git_issue_642onesheet.xls create mode 100644 src/TestData/Test_git_issue_642onesheet.xlsb create mode 100644 src/TestData/Test_git_issue_642onesheet.xlsx diff --git a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs index 9fb5b70d..8a58d19b 100644 --- a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs +++ b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs @@ -77,12 +77,14 @@ internal XlsWorkbook(Stream stream, string password, Encoding fallbackEncoding) public int ResultsCount => Sheets?.Count ?? -1; - public static bool IsRawBiffStream(byte[] bytes) - { - if (bytes.Length < 8) + public int ActiveSheet { get; private set; } + + public static bool IsRawBiffStream(byte[] bytes) { - throw new ArgumentException("Needs at least 8 bytes to probe", nameof(bytes)); - } + if (bytes.Length < 8) + { + throw new ArgumentException("Needs at least 8 bytes to probe", nameof(bytes)); + } ushort rid = BitConverter.ToUInt16(bytes, 0); ushort size = BitConverter.ToUInt16(bytes, 2); @@ -203,15 +205,19 @@ private void ReadWorkbookGlobals(XlsBiffStream biffStream) ExtSST = rec; break; - // case BIFFRECORDTYPE.PROTECT: - // case BIFFRECORDTYPE.PROT4REVPASSWORD: + case XlsBiffRecord _ when rec.Id == BIFFRECORDTYPE.WINDOW1: + ActiveSheet = rec.ReadInt16(10); + break; + + // case BIFFRECORDTYPE.PROTECT: + // case BIFFRECORDTYPE.PROT4REVPASSWORD: // IsProtected // break; - // case BIFFRECORDTYPE.PASSWORD: - default: - break; + // case BIFFRECORDTYPE.PASSWORD: + default: + break; + } } - } SST?.Flush(); diff --git a/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs b/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs index 38fe95bc..273e612b 100644 --- a/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs +++ b/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs @@ -42,7 +42,12 @@ public XlsWorksheet(XlsWorkbook workbook, XlsBiffBoundSheet refSheet, Stream str /// public string VisibleState { get; } - public HeaderFooter HeaderFooter { get; private set; } + /// + /// Gets the activity of worksheet. + /// + 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..81202775 100644 --- a/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs +++ b/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs @@ -7,7 +7,9 @@ internal sealed class CsvWorkbook(Stream stream, Encoding encoding, char[] autod { public int ResultsCount => 1; - public Stream Stream { get; } = stream; + 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..f933be29 100644 --- a/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs +++ b/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs @@ -40,7 +40,11 @@ public CsvWorksheet(Stream stream, Encoding fallbackEncoding, char[] autodetectS public string VisibleState => null; - public HeaderFooter HeaderFooter => null; + public int ActiveSheet => 0; + + public bool IsActiveSheet => true; + + public HeaderFooter HeaderFooter => null; public CellRange[] MergeCells => null; diff --git a/src/ExcelDataReader/Core/IWorkbook.cs b/src/ExcelDataReader/Core/IWorkbook.cs index 251a213d..4c5b30d6 100644 --- a/src/ExcelDataReader/Core/IWorkbook.cs +++ b/src/ExcelDataReader/Core/IWorkbook.cs @@ -13,5 +13,8 @@ internal interface IWorkbook IEnumerable ReadWorksheets(); - NumberFormatString GetNumberFormatString(int index); + int ActiveSheet { get; } + + NumberFormatString GetNumberFormatString(int index); + } } diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs index eea1153d..80367d87 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs @@ -2,16 +2,16 @@ #nullable enable -namespace ExcelDataReader.Core.OpenXmlFormat.BinaryFormat; - -internal sealed class BiffWorkbookReader(Stream stream, Dictionary worksheetRels) : BiffReader(stream) +namespace ExcelDataReader.Core.OpenXmlFormat.BinaryFormat { - private const int WorkbookPr = 0x99; - private const int Sheet = 0x9C; - - private readonly Dictionary _worksheetRels = worksheetRels; + internal sealed class BiffWorkbookReader(Stream stream, Dictionary worksheetRels) : BiffReader(stream) + { + private const int WorkbookPr = 0x99; + private const int Sheet = 0x9C; + private const int BrtBookView = 0x9e; + private readonly Dictionary _worksheetRels = worksheetRels; - private enum SheetVisibility : byte + private enum SheetVisibility : byte { Visible = 0x0, Hidden = 0x1, @@ -41,9 +41,17 @@ protected override Record ReadOverride(byte[] buffer, uint recordId, uint record uint nameLength = GetDWord(buffer, offset); string name = GetString(buffer, offset + 4, nameLength); - return new SheetRecord(name, id, rid, state, rid != null && _worksheetRels.TryGetValue(rid, out var path) ? path : null); - default: - return Record.Default; + 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..22a8b23a --- /dev/null +++ b/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs @@ -0,0 +1,18 @@ +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..648f8f94 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs @@ -22,32 +22,38 @@ public XlsxWorkbook(ZipWorker zipWorker) public int ResultsCount => Sheets?.Count ?? -1; - public IEnumerable ReadWorksheets() - { - foreach (var sheet in Sheets) + public int ActiveSheet { get; private set; } + + public IEnumerable ReadWorksheets() { - yield return new XlsxWorksheet(_zipWorker, this, sheet); + foreach (var sheet in Sheets) + { + yield return new XlsxWorksheet(_zipWorker, this, sheet); + } } - } - private void ReadWorkbook() - { - using RecordReader reader = _zipWorker.GetWorkbookReader(); - - Record record; - while ((record = reader.Read()) != null) - { - switch (record) - { - case WorkbookPrRecord pr: - IsDate1904 = pr.Date1904; - break; - case SheetRecord sheet: - Sheets.Add(sheet); - break; + private void ReadWorkbook() + { + using RecordReader reader = _zipWorker.GetWorkbookReader(); + + Record record; + while ((record = reader.Read()) != null) + { + switch (record) + { + case WorkbookPrRecord pr: + IsDate1904 = pr.Date1904; + break; + case SheetRecord sheet: + Sheets.Add(sheet); + break; + case WorkbookActRecord activeSheet: + ActiveSheet = activeSheet.ActiveSheet; + break; + + } } } - } private void ReadSharedStrings() { diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs index 35455f69..17e8a0bb 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs @@ -91,7 +91,9 @@ public XlsxWorksheet(ZipWorker document, XlsxWorkbook workbook, SheetRecord refS public string VisibleState { get; } - public HeaderFooter HeaderFooter { 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..6568a2f4 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs @@ -47,31 +47,56 @@ protected override IEnumerable ReadOverride() continue; } - while (!Reader.EOF) + while (!Reader.EOF) + { + if (Reader.IsStartElement(ElementSheet, ProperNamespaces.NsSpreadsheetMl)) + { + var rid = Reader.GetAttribute(AttributeRelationshipId, ProperNamespaces.NsDocumentRelationship); + yield return new SheetRecord( + Reader.GetAttribute(AttributeName), + uint.Parse(Reader.GetAttribute(AttributeSheetId), CultureInfo.InvariantCulture), + rid, + Reader.GetAttribute(AttributeVisibleState), + rid != null && _worksheetsRels.TryGetValue(rid, out var path) ? path : null); + Reader.Skip(); + } + else if (!XmlReaderHelper.SkipContent(Reader)) + { + break; + } + } + } + + else if (Reader.IsStartElement("bookViews", ProperNamespaces.NsSpreadsheetMl)) { - if (Reader.IsStartElement(ElementSheet, ProperNamespaces.NsSpreadsheetMl)) + if (!XmlReaderHelper.ReadFirstContent(Reader)) { - var rid = Reader.GetAttribute(AttributeRelationshipId, ProperNamespaces.NsDocumentRelationship); - yield return new SheetRecord( - Reader.GetAttribute(AttributeName), - uint.Parse(Reader.GetAttribute(AttributeSheetId), CultureInfo.InvariantCulture), - rid, - Reader.GetAttribute(AttributeVisibleState), - rid != null && _worksheetsRels.TryGetValue(rid, out var path) ? path : null); - Reader.Skip(); + continue; } - else if (!XmlReaderHelper.SkipContent(Reader)) + + while (!Reader.EOF) { - break; + if (Reader.IsStartElement("workbookView", ProperNamespaces.NsSpreadsheetMl)) + { + string activeTab = Reader.GetAttribute("activeTab"); + int result = -1; + int.TryParse(activeTab, out result); + yield return new WorkbookActRecord(result); + Reader.Skip(); + } + else if (!XmlReaderHelper.SkipContent(Reader)) + { + break; + } } + + } + else if (!XmlReaderHelper.SkipContent(Reader)) + { + yield break; } - } - else if (!XmlReaderHelper.SkipContent(Reader)) - { - yield break; } } - } private bool CheckStartElementAndApplyNamespaces(string element) { diff --git a/src/ExcelDataReader/ExcelDataReader.cs b/src/ExcelDataReader/ExcelDataReader.cs index 8c0ffdc9..cb037870 100644 --- a/src/ExcelDataReader/ExcelDataReader.cs +++ b/src/ExcelDataReader/ExcelDataReader.cs @@ -28,7 +28,13 @@ internal abstract class ExcelDataReader : IExcelDataReade public string VisibleState => _worksheetIterator?.Current?.VisibleState; - public HeaderFooter HeaderFooter => _worksheetIterator?.Current?.HeaderFooter; + private int _idx = 0; + + 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. public CellRange[] MergeCells => _worksheetIterator?.Current?.MergeCells; @@ -213,7 +219,9 @@ public void Reset() _worksheetIterator = null; _rowIterator = null; - ResetSheetData(); + _idx = 0; + + ResetSheetData(); if (Workbook != null) { @@ -262,9 +270,12 @@ public bool NextResult() return false; } - _rowIterator = _worksheetIterator.Current.ReadRows().GetEnumerator(); - return true; - } + _rowIterator = _worksheetIterator.Current.ReadRows().GetEnumerator(); + + _idx++; + + return true; + } public bool Read() { diff --git a/src/ExcelDataReader/IExcelDataReader.cs b/src/ExcelDataReader/IExcelDataReader.cs index f14a4ff1..0498408b 100644 --- a/src/ExcelDataReader/IExcelDataReader.cs +++ b/src/ExcelDataReader/IExcelDataReader.cs @@ -22,10 +22,20 @@ public interface IExcelDataReader : IDataReader /// string VisibleState { get; } - /// - /// Gets the sheet header and footer -or- if none set. - /// - HeaderFooter HeaderFooter { get; } + /// + /// Gets the active sheet. + /// + int ActiveSheet { get; } + + /// + /// Gets the activity of worksheet. + /// + bool IsActiveSheet { get; } + + /// + /// Gets the sheet header and footer -or- if none set. + /// + HeaderFooter HeaderFooter { get; } /// /// Gets the list of merged cell ranges. diff --git a/src/TestData/Test10x10.xls b/src/TestData/Test10x10.xls index cb1ea6ebc081dd4d0f75845bbdc6a3ca3d65b7a6..1aaaa438831c259316089e8fb5d3a81a3d68775e 100644 GIT binary patch delta 38 ucmZq3X~@~2!NkGHz~Gpll$kf#kSTrhbf&9@>;f9QYP1gB++1wZ%LoAQR1HP| delta 38 ucmZq3X~@~2!NkGJzz~$5l$kf#kSTrhbf&9@>>qSKCiF(z9lFW5xnxEUBLKD-3%39O delta 28 kcmZqJ!ql*ZX@dq63qJ#c*Jd3ihiQ}Vns96`nbE@t0D-d!Qvd(} diff --git a/src/TestData/TestUnicodeChars.xls b/src/TestData/TestUnicodeChars.xls index a5b4c5dad9cabf88040b3976bd76c6b6e37878ec..8890b6946854b35eb9757b80fe39a7215b54ed0b 100644 GIT binary patch delta 38 ucmZoDXeijA!NkGHz~Gpll$kf#kSTrhbf&Au>>JLX6xBX-b91p-FCzf`LJmRz delta 38 ucmZoDXeijA!NkGJzz~$5l$kf#kSTrhbf&Au>>qUaug9lE)>IH{Kr0I=cby+wzm+18{0|2fd3Q_<7 diff --git a/src/TestData/Test_git_issue_45.xls b/src/TestData/Test_git_issue_45.xls index 1f9798ec0ea3baa3a561db959ae6d2c2d9bca1ce..354bc20f2418dc5584741f1604eea90a3143e267 100644 GIT binary patch delta 31 ncmZoT!_;twX@dq63nK%A<7OQu$3<*~ZF{v2-P~Nfq?Zu@r6~(o delta 31 ncmZoT!_;twX@dq63qJ#c*Jd3i$3<+f7cJpsy|lS_NiQP+rYQ>x diff --git a/src/TestData/Test_git_issue_642.xls b/src/TestData/Test_git_issue_642.xls new file mode 100644 index 0000000000000000000000000000000000000000..085d9e7428d1842fc3e23c2cc6f2e1853953d22e GIT binary patch literal 37888 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 RMMSfon{IlAXY#-P`+wV33LXFe literal 0 HcmV?d00001 diff --git a/src/TestData/Test_git_issue_642.xlsb b/src/TestData/Test_git_issue_642.xlsb new file mode 100644 index 0000000000000000000000000000000000000000..b16f6fbb46db68683e91128ece6e9ccf436ed906 GIT binary patch literal 12873 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 Wfb)C-1vKyA2>rj2F6an2xc>sEaKZxs literal 0 HcmV?d00001 diff --git a/src/TestData/Test_git_issue_642.xlsx b/src/TestData/Test_git_issue_642.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8a38c4f522377a5fea20dd53ccc763b8c0b2f404 GIT binary patch literal 11981 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 E1N~-;g8%>k literal 0 HcmV?d00001 diff --git a/src/TestData/Test_git_issue_642onesheet.xls b/src/TestData/Test_git_issue_642onesheet.xls new file mode 100644 index 0000000000000000000000000000000000000000..92295fc2d03b5ea63c435d1c15f996f1db729505 GIT binary patch literal 26112 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 a{LjRT&?hAT=lc!^6=0UI9_WUAzxzM0W(6?- literal 0 HcmV?d00001 diff --git a/src/TestData/Test_git_issue_642onesheet.xlsx b/src/TestData/Test_git_issue_642onesheet.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a7f08112f9b623bf877b5eb4de822abf0c936f18 GIT binary patch literal 7716 zcmeHM1zVKc+8!EIV1S{!2apmJknW+RTR=J|=ms4SkZvhyq+7Z~x+J6pNhtvdl~VXd z_t~Fr_ddVi>^0ZC>z(VJ`?+S;x}Uh8rJ;g>2?AgPZ~*`SBOqV9XY?K#0Dy}L0FVH1 z(T!!{FgI(Mo0*O`!rIk@#|!Gjkc){9&IX{PzW?v|A6|j7grR#~d_egV$XD5Q?(oGL zDcsw8$Ufp`F#Us7C5tIu?}dp=agvj2sT@)Vp=yiK1y^x_%;7uJAjhzxw$j;@7z4D5 zFUl?T7FiJuM{%Sn`Pvg+IHYgUYvw+Ev8kEGeu=E1)hkos5W{=qj-$tPf=2U9P{Js6 zOQ@cy--p8(c#=0BRMe)jtd;76f9F}N6tg}XX(jdiJCP?3Vo66^US{g@QzFOfRM&ON zo}J5coM7Hd6~PvzJ&1PVR92$`ig+*>;ux?q*$zO_6d z6jdR8=3`xqP9uw1T!jIdK4^8}suMkx=p5|?AKhxNiMtFiJdKQz`-UCY@zl8gmhHoo zdhCOF>(Si8{>e)3PiXco%wMwtC`Ke&Zm?kT+Y1wHY9z$SrJYAqUjNpjm5n{i)Umcr zh+@#y6$U`#FK$?;%g6i)^+XlrW!F(|Xy#(=)_i(v8bJX#xJXa zWf8<6QRaHj@{p(J&h9vzY%a+P&SfjzL>^P0r@o{rDtm)H+G03M8;f#O29}r=-W|%X#JF?FEpFV=>%;IFt79A zTJhY?rz7Hr@tKeH&S{)Y zBOD2O#A)08#Sn@trF}I$!s=v{Vi)U219(Yw@m+Q%jbnN;IAm8R|>FMaj3rkY1s<$Z~st~)de z)UJQsFi<%njU+WH5Ntf)KIjYe$;ZGlkj+G8inhotrBh`LWQOC6sa*g&*E;$yNKCdtSy znd5rw_t-#;c#)e+db!oiMk7n;`U17^bi5h&AfPehW6{sqiIZZ6`-yj)xZ=L>hbda! zFq57^QbXOhd4Pk|;eI_r2W<=#VB2FYh!>R=dzrmNb+f3v7E`LbHzN&*qpiDG;wAk0 zbLhSiqq9(0?pUGemp9>xve&?dT#Hm{6f6AvnIY!0)=b65+b_Wz%cfkRRF@i8W}kPe zKXOIw$CCQlKSpy3zgO=RZPrUtUl%j1n}7ROfp^V749z-Hr2bU}pw_wHwHCe9xT_QP zdfle7?d$bU%8x0&5PvW{>?)Bl-E-tC%kqOjym_^OL&G8&daZE%H8(Q)^Y^xzjhqE5 zP8(~NRbPm@D_n|vDb&_34+M7GuMQ&vSV~U5NgI$ai+`x-t#M5zPQbk}T!ZWL@R0X4 zi(-09j(TL5@Rq>x7G2rUhH&mHrUG8+^(E7@nepz?!b6rclh6c1L45X;ai-z5Qop#R zo~28;cHa2FDUX}3-f&}`xnE4>z_me9o9Y1^83T4Ie!ab@KNMHFFg9<3*+Kw2jeyph5oczb-g(6v{deVHm2peVs#r3{GIxClYBAP`_Xi7W>fjW9*(M_F!bkS1p$u%%!B=T7aEpU1`SJ3lnKG)pj6a$X7a?pZD3gcy6FAJ^ZAkI9M`i0%?%4 zlLU8AQ?g%R>-+}B?QLgJU5(}=>FJQd9B)wDBUPU4u3=pph8dG7(|EigXhY1vaI$4L~I*4 zRfcWd6otKHc5Rh%8YlW?Y@`f6#L11pdPG*7ic^jCh&oT}T^0A*Z;xYZE#&4tSoeb> zFWQjOjjy*wq2C?=x`iddQ5j%_6`s|w7Po6k_Ph4EX$B=3XHnIbecn?2G^Z{wozuZv zj9RN=H@p$I;qiA#tQmS_!4CCCE#oCr?JCyXRuU@83VO=K`!d@MI2f-uz?(A_D-wF@ zv+36c6xLsvCUb;LyDQD610isaR@myPtWjS8KN+;7(2s$@N|MN5n68Nqa=34P^6l{Q z@a_KQ+P8fiH{BSSp%CYxb$BpF%L0uyQKPcK%x7pkL7}vQvQb>f+w`tc5dEI#3#+Iw zg3@=jqSnT8;W1y5N)3cPY{#P7ZjIUSS>DcS2vly!ZcV9J2H@re;#Sm;c3VdWzg#{daZCCM>R5(c|<&-^aJ6t!0MQqyzB*}Rf@r&ysNEB9!VC1|Yr|XK} zyrgn97u)Cd)=TPpgu}7JdWkCDY7g(KeE8VzN>qLb2%SYg3oFlQrbS4Y4s9CJrxCRP zW1KOULY#@22r37_l8`aFEqwcS9kACKFs{Fz351-V6ROR!39ZHWFpq$o&zhQT!L_$o zR#gQ*+1GHtSbID+Gy26Ar?#+`y*9{}aQTem>y^PEfk(IuQY}@T#F2cw8Q+9(wT-8J z2pYlEyCS>D(@!tj5x^fay&;pB#ZcPszCL;%#=|$uF(8qk37;!oYmA;T*m(*V)OX%f ze(UI~;!phW+@HeRA)@3BbUKaOGg?<_J5SoPbb^!oc57}&tA%S9X$O@lQtR96Ywd3i zJ0rf|w>lmu6|9T?xNSgIf~dF)b5~fw*(~#h(&YCmcX<_Bf^S$hn+MO_FVyB3NwNj^ zhgjD?;R$i>9!>LQY$jo+1p<*EXJSLYyS=iLp>iigtZl&t=~f^qrf5x&fE`cbwP!Mv zm*HlL5=w+QwTZvu22&Ly0|PDw%*-UJF1@}^WyMGctfhuueGVlI^gb`9#0?s0J@|SYw3JCB zcD0(f)ZuIOP>6U^d&PvcRETqxLS#KD|yZI$c1GEX7{)3k%(nb~fOcehk0BLdv2~^lba+`V{Cq^1|L$`5mdYW*lQ?Sl89NE7Cq|Bm z_L4;Si?Mok%Lk?wTH81V0>^jdi4>V>%wbR9hXlPLj^x6uGw%<5gm;8sHpRl=Xn}6Zyugvj*>O`V;%QX zw$(=i{d@Kk`p(c^#tCLs%Tn=|k*QV{tbC@dTlx?051TLLzR^%*%h*kEgXIeA#g%*Y z5o!qEnODt~>S^!uV33b<7vEqz)hinX9uhc9jXzmwm#-q*fAeIS?V!X#E16++h=srV z=vMUSGvNZ+VAdfi%CT87mhs}rfXKf_7oGilPBrP6KR?LyNamZuV{{Xb7TVpb>)wXaFy}s{b{xH^`k~#6U8}lGGGiQWC z*q|gOqoN0o-sBBEoyX_$MP!=yo6%K`6|knY$;w{s)PKll$Eq5T0TIe&OvnbwyU8P7 z-=1I>NhqNNL`2AATg6r28LQJ|QkbMU*oM8Z366&~6Dd<++dbTzE2b;Yuq|^mgxo$3 z<|3!Q-cZsC7%fE#cRo2$Lmt-QW@gdK7k?xfeKrFdk z?N!IM+DMXK<1nn^F4paQCm?Y&Nkn`VEMkSX_K`xoY(BJy82F0X@jkxYdo=+9WCac- zVQAy%R23KZt2f(Re13zZy_H&Dr#fYBWlA+R1a)Rk%PpI53by& z#M^YPk8pWSNL@NVnEcvh{u15Sm-ll*G4fnSqg}WdZ-3HCq zx9>OHcWUX8DK%^+eEb;4d|VCga2FvVUwC{$HP`N9OTrVq%=2lDkAA2$rB(3$yE%}` z{4+HlT}$_#s7NRg!!uvgVr{OOG}8(_n48PJQp1rczsgic?|_0S`!iVGbQ9dTg?LwR zuzjtrRHSn2LZpui_qep~eh)_y)x82usA$y1vGt8ev4WT;-j{6?6`wo)v3}doEn2ff zJqbqDa1=jPf~%diwVNyNkLgFoi;VxNoyH`R6C_AhgJp0>ay8E9hmK~+;sh1a1V@%U zcVo5s*+ioRT^moLH-_jxb_;b|@q=74-fKzm8O)7=j*czGeG-}C2$DP~vSV=|E=`T? zX&?Dv+SjF!c#NbqtUET!tZ$Gr99;zH9g`GoY&@2ETIACDU9p}H@tY@uj@0XMd=XFi zsw&DKk)}Nhl~ROY7C!l0IPI}5cf3KIG2`6wK>^#pFKptjN+=rVW1(6#626^@>W^86 z<#*V>Xk@Ik-6kTLPUOEw`#94#v!vK!!ot+hW2}#dY4Wijl}cR-;fkN(Y+<0Sbh950 zPDu7DtMqjpCdwL3&8jc*0F)Np|#TzM_wF4q6)M*l1AQM8SWANk&m4gLse zm0kC>V3+1LP~r@jBuR<|qdV8`LLG@jlcA7{FTzUHTAh#9_9HueBi@a<3U@ zRVG0)jkOYK!BsxhnD)+qT{7)EcNAow5G;@7Hn)Nsb6Tqptf(=jRS=pC_KHm$X z`91t_!kbux-GD+-y2GIpuxj(KK~OZ?;NdFtQOx5To-b!3<=o`1NidBRmisZZ1_jUO7_4@`!)ln40iVQ zXf$+j?=@j{xdS-JMU5R14BM0d4(o@?54h^OX?j}0$2q&HE*Sm4dFQjeHi53R-&Z0INWfGF1v(`No6Bd3oyXuXbJX zIzsRj=-kUGp!V{gJ{u$x6bC;?mWJcacb;wWl9 z*QPwS3M$ha2Zshv&7VIng+eIaA1`eNwiGUYT%_+D7$ayurL&e_~^hN-@~>}NFfw7k1z@@aBLx``X_VyCy!%$}0$Qiib= zc8maTIFjb%96pCeu8gZ{{jBltoa@KyA1vb#Ul7eQT)4s-%E?1297}g z|HJ=QX#8&FuPN}S4FDjP3;_6BLi`>6*J$=D9Fy`F_+Vvk;C01#vk00000 From 3191c0f4eaec1eb6017193e14f31e0318d7cb22c Mon Sep 17 00:00:00 2001 From: John Opincar Date: Fri, 23 Aug 2024 15:36:06 -0500 Subject: [PATCH 2/8] Fix formatting; --- .../Core/BinaryFormat/XlsWorkbook.cs | 34 ++++----- .../Core/BinaryFormat/XlsWorksheet.cs | 10 +-- .../Core/CsvFormat/CsvWorkbook.cs | 4 +- .../Core/CsvFormat/CsvWorksheet.cs | 6 +- src/ExcelDataReader/Core/IWorkbook.cs | 7 +- .../BinaryFormat/BiffWorkbookReader.cs | 31 ++++---- .../Records/WorkbookActRecord.cs | 15 ++-- .../Core/OpenXmlFormat/XlsxWorkbook.cs | 49 ++++++------ .../Core/OpenXmlFormat/XlsxWorksheet.cs | 4 +- .../XmlFormat/XmlWorkbookReader.cs | 74 +++++++++---------- src/ExcelDataReader/ExcelDataReader.cs | 23 +++--- src/ExcelDataReader/IExcelDataReader.cs | 28 +++---- 12 files changed, 138 insertions(+), 147 deletions(-) diff --git a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs index 8a58d19b..7b712379 100644 --- a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs +++ b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs @@ -77,14 +77,14 @@ internal XlsWorkbook(Stream stream, string password, Encoding fallbackEncoding) public int ResultsCount => Sheets?.Count ?? -1; - public int ActiveSheet { get; private set; } + public int ActiveSheet { get; private set; } - public static bool IsRawBiffStream(byte[] bytes) + public static bool IsRawBiffStream(byte[] bytes) + { + if (bytes.Length < 8) { - if (bytes.Length < 8) - { - throw new ArgumentException("Needs at least 8 bytes to probe", nameof(bytes)); - } + throw new ArgumentException("Needs at least 8 bytes to probe", nameof(bytes)); + } ushort rid = BitConverter.ToUInt16(bytes, 0); ushort size = BitConverter.ToUInt16(bytes, 2); @@ -205,19 +205,19 @@ private void ReadWorkbookGlobals(XlsBiffStream biffStream) ExtSST = rec; break; - case XlsBiffRecord _ when rec.Id == BIFFRECORDTYPE.WINDOW1: - ActiveSheet = rec.ReadInt16(10); - break; + case XlsBiffRecord _ when rec.Id == BIFFRECORDTYPE.WINDOW1: + ActiveSheet = rec.ReadInt16(10); + break; - // case BIFFRECORDTYPE.PROTECT: - // case BIFFRECORDTYPE.PROT4REVPASSWORD: - // IsProtected - // break; - // case BIFFRECORDTYPE.PASSWORD: - default: - break; - } + // case BIFFRECORDTYPE.PROTECT: + // case BIFFRECORDTYPE.PROT4REVPASSWORD: + // IsProtected + // break; + // case BIFFRECORDTYPE.PASSWORD: + default: + break; } + } SST?.Flush(); diff --git a/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs b/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs index 273e612b..c3239d2f 100644 --- a/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs +++ b/src/ExcelDataReader/Core/BinaryFormat/XlsWorksheet.cs @@ -42,12 +42,12 @@ public XlsWorksheet(XlsWorkbook workbook, XlsBiffBoundSheet refSheet, Stream str /// public string VisibleState { get; } - /// - /// Gets the activity of worksheet. - /// - public bool IsActiveSheet { get; } + /// + /// Gets a value indicating whether the sheet is active. + /// + public bool IsActiveSheet { get; } - public HeaderFooter HeaderFooter { get; private set; } + 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 81202775..f1af684d 100644 --- a/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs +++ b/src/ExcelDataReader/Core/CsvFormat/CsvWorkbook.cs @@ -7,9 +7,9 @@ internal sealed class CsvWorkbook(Stream stream, Encoding encoding, char[] autod { public int ResultsCount => 1; - public int ActiveSheet => 0; + public int ActiveSheet => 0; - public Stream Stream { get; } = stream; + 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 f933be29..835f3b27 100644 --- a/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs +++ b/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs @@ -40,11 +40,11 @@ public CsvWorksheet(Stream stream, Encoding fallbackEncoding, char[] autodetectS public string VisibleState => null; - public int ActiveSheet => 0; + public int ActiveSheet => 0; - public bool IsActiveSheet => true; + public bool IsActiveSheet => true; - public HeaderFooter HeaderFooter => null; + public HeaderFooter HeaderFooter => null; public CellRange[] MergeCells => null; diff --git a/src/ExcelDataReader/Core/IWorkbook.cs b/src/ExcelDataReader/Core/IWorkbook.cs index 4c5b30d6..1ed4f34b 100644 --- a/src/ExcelDataReader/Core/IWorkbook.cs +++ b/src/ExcelDataReader/Core/IWorkbook.cs @@ -11,10 +11,9 @@ internal interface IWorkbook { int ResultsCount { get; } - IEnumerable ReadWorksheets(); + int ActiveSheet { get; } - int ActiveSheet { get; } + IEnumerable ReadWorksheets(); - NumberFormatString GetNumberFormatString(int index); - } + NumberFormatString GetNumberFormatString(int index); } diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs index 80367d87..2f1603ff 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs @@ -2,16 +2,16 @@ #nullable enable -namespace ExcelDataReader.Core.OpenXmlFormat.BinaryFormat +namespace ExcelDataReader.Core.OpenXmlFormat.BinaryFormat; + +internal sealed class BiffWorkbookReader(Stream stream, Dictionary worksheetRels) : BiffReader(stream) { - internal sealed class BiffWorkbookReader(Stream stream, Dictionary worksheetRels) : BiffReader(stream) - { - private const int WorkbookPr = 0x99; - private const int Sheet = 0x9C; - private const int BrtBookView = 0x9e; - private readonly Dictionary _worksheetRels = worksheetRels; + private const int WorkbookPr = 0x99; + private const int Sheet = 0x9C; + private const int BrtBookView = 0x9e; + private readonly Dictionary _worksheetRels = worksheetRels; - private enum SheetVisibility : byte + private enum SheetVisibility : byte { Visible = 0x0, Hidden = 0x1, @@ -41,17 +41,14 @@ protected override Record ReadOverride(byte[] buffer, uint recordId, uint record uint nameLength = GetDWord(buffer, offset); 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); - + 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; - } + default: + return Record.Default; } } } diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs b/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs index 22a8b23a..f5723504 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/Records/WorkbookActRecord.cs @@ -4,15 +4,14 @@ using System.Text; using System.Threading.Tasks; -namespace ExcelDataReader.Core.OpenXmlFormat.Records +namespace ExcelDataReader.Core.OpenXmlFormat.Records; + +internal sealed class WorkbookActRecord : Record { - internal sealed class WorkbookActRecord : Record + public WorkbookActRecord(int activeSheet) { - public WorkbookActRecord(int activeSheet) - { - this.ActiveSheet = activeSheet; - } - - public int ActiveSheet { get; } + this.ActiveSheet = activeSheet; } + + public int ActiveSheet { get; } } diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs index 648f8f94..c5d932fb 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs @@ -22,38 +22,37 @@ public XlsxWorkbook(ZipWorker zipWorker) public int ResultsCount => Sheets?.Count ?? -1; - public int ActiveSheet { get; private set; } + public int ActiveSheet { get; private set; } - public IEnumerable ReadWorksheets() + public IEnumerable ReadWorksheets() + { + foreach (var sheet in Sheets) { - foreach (var sheet in Sheets) - { - yield return new XlsxWorksheet(_zipWorker, this, sheet); - } + yield return new XlsxWorksheet(_zipWorker, this, sheet); } + } - private void ReadWorkbook() - { - using RecordReader reader = _zipWorker.GetWorkbookReader(); + private void ReadWorkbook() + { + using RecordReader reader = _zipWorker.GetWorkbookReader(); - Record record; - while ((record = reader.Read()) != null) - { - switch (record) - { - case WorkbookPrRecord pr: - IsDate1904 = pr.Date1904; - break; - case SheetRecord sheet: - Sheets.Add(sheet); - break; - case WorkbookActRecord activeSheet: - ActiveSheet = activeSheet.ActiveSheet; - break; - - } + Record record; + while ((record = reader.Read()) != null) + { + switch (record) + { + case WorkbookPrRecord pr: + IsDate1904 = pr.Date1904; + break; + case SheetRecord sheet: + Sheets.Add(sheet); + break; + case WorkbookActRecord activeSheet: + ActiveSheet = activeSheet.ActiveSheet; + break; } } + } private void ReadSharedStrings() { diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs index 17e8a0bb..dc794872 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorksheet.cs @@ -91,9 +91,9 @@ public XlsxWorksheet(ZipWorker document, XlsxWorkbook workbook, SheetRecord refS public string VisibleState { get; } - public bool IsActiveSheet { get; } + public bool IsActiveSheet { get; } - public HeaderFooter HeaderFooter { 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 6568a2f4..7a0a5078 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs @@ -47,56 +47,54 @@ protected override IEnumerable ReadOverride() continue; } - while (!Reader.EOF) + while (!Reader.EOF) + { + if (Reader.IsStartElement(ElementSheet, ProperNamespaces.NsSpreadsheetMl)) + { + var rid = Reader.GetAttribute(AttributeRelationshipId, ProperNamespaces.NsDocumentRelationship); + yield return new SheetRecord( + Reader.GetAttribute(AttributeName), + uint.Parse(Reader.GetAttribute(AttributeSheetId), CultureInfo.InvariantCulture), + rid, + Reader.GetAttribute(AttributeVisibleState), + rid != null && _worksheetsRels.TryGetValue(rid, out var path) ? path : null); + Reader.Skip(); + } + else if (!XmlReaderHelper.SkipContent(Reader)) { - if (Reader.IsStartElement(ElementSheet, ProperNamespaces.NsSpreadsheetMl)) - { - var rid = Reader.GetAttribute(AttributeRelationshipId, ProperNamespaces.NsDocumentRelationship); - yield return new SheetRecord( - Reader.GetAttribute(AttributeName), - uint.Parse(Reader.GetAttribute(AttributeSheetId), CultureInfo.InvariantCulture), - rid, - Reader.GetAttribute(AttributeVisibleState), - rid != null && _worksheetsRels.TryGetValue(rid, out var path) ? path : null); - Reader.Skip(); - } - else if (!XmlReaderHelper.SkipContent(Reader)) - { - break; - } + break; } } + } + else if (Reader.IsStartElement("bookViews", ProperNamespaces.NsSpreadsheetMl)) + { + if (!XmlReaderHelper.ReadFirstContent(Reader)) + { + continue; + } - else if (Reader.IsStartElement("bookViews", ProperNamespaces.NsSpreadsheetMl)) + while (!Reader.EOF) { - if (!XmlReaderHelper.ReadFirstContent(Reader)) + if (Reader.IsStartElement("workbookView", ProperNamespaces.NsSpreadsheetMl)) { - continue; + string activeTab = Reader.GetAttribute("activeTab"); + int result = -1; + int.TryParse(activeTab, out result); + yield return new WorkbookActRecord(result); + Reader.Skip(); } - - while (!Reader.EOF) + else if (!XmlReaderHelper.SkipContent(Reader)) { - if (Reader.IsStartElement("workbookView", ProperNamespaces.NsSpreadsheetMl)) - { - string activeTab = Reader.GetAttribute("activeTab"); - int result = -1; - int.TryParse(activeTab, out result); - yield return new WorkbookActRecord(result); - Reader.Skip(); - } - else if (!XmlReaderHelper.SkipContent(Reader)) - { - break; - } + break; } - - } - else if (!XmlReaderHelper.SkipContent(Reader)) - { - yield break; } } + else if (!XmlReaderHelper.SkipContent(Reader)) + { + yield break; + } } + } private bool CheckStartElementAndApplyNamespaces(string element) { diff --git a/src/ExcelDataReader/ExcelDataReader.cs b/src/ExcelDataReader/ExcelDataReader.cs index cb037870..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,13 +29,11 @@ internal abstract class ExcelDataReader : IExcelDataReade public string VisibleState => _worksheetIterator?.Current?.VisibleState; - private int _idx = 0; + public int ActiveSheet => this.Workbook.ActiveSheet; - public int ActiveSheet => this.Workbook.ActiveSheet; + public bool IsActiveSheet => _idx == this.Workbook.ActiveSheet; - public bool IsActiveSheet => _idx == this.Workbook.ActiveSheet; - - public HeaderFooter HeaderFooter => _worksheetIterator?.Current?.HeaderFooter; + public HeaderFooter HeaderFooter => _worksheetIterator?.Current?.HeaderFooter; // We shouldn't expose the internal array here. public CellRange[] MergeCells => _worksheetIterator?.Current?.MergeCells; @@ -219,13 +218,13 @@ public void Reset() _worksheetIterator = null; _rowIterator = null; - _idx = 0; + _idx = 0; - ResetSheetData(); + ResetSheetData(); if (Workbook != null) { - _worksheetIterator = ReadWorksheetsWithCache().GetEnumerator(); // Workbook.ReadWorksheets().GetEnumerator(); + _worksheetIterator = ReadWorksheetsWithCache().GetEnumerator(); if (!_worksheetIterator.MoveNext()) { _worksheetIterator.Dispose(); @@ -270,12 +269,12 @@ public bool NextResult() return false; } - _rowIterator = _worksheetIterator.Current.ReadRows().GetEnumerator(); + _rowIterator = _worksheetIterator.Current.ReadRows().GetEnumerator(); - _idx++; + _idx++; - return true; - } + return true; + } public bool Read() { diff --git a/src/ExcelDataReader/IExcelDataReader.cs b/src/ExcelDataReader/IExcelDataReader.cs index 0498408b..13f48a3b 100644 --- a/src/ExcelDataReader/IExcelDataReader.cs +++ b/src/ExcelDataReader/IExcelDataReader.cs @@ -22,20 +22,20 @@ public interface IExcelDataReader : IDataReader /// string VisibleState { get; } - /// - /// Gets the active sheet. - /// - int ActiveSheet { get; } - - /// - /// Gets the activity of worksheet. - /// - bool IsActiveSheet { get; } - - /// - /// Gets the sheet header and footer -or- if none set. - /// - HeaderFooter HeaderFooter { 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. + /// + HeaderFooter HeaderFooter { get; } /// /// Gets the list of merged cell ranges. From 5b8af55f9a398988fdcfd2e4cf2eb677bfa887e3 Mon Sep 17 00:00:00 2001 From: John Opincar Date: Fri, 23 Aug 2024 15:45:35 -0500 Subject: [PATCH 3/8] More formatting fixes; --- src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs | 4 ++-- .../Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs | 1 + src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs index 7b712379..8e578bdc 100644 --- a/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs +++ b/src/ExcelDataReader/Core/BinaryFormat/XlsWorkbook.cs @@ -211,8 +211,8 @@ private void ReadWorkbookGlobals(XlsBiffStream biffStream) // case BIFFRECORDTYPE.PROTECT: // case BIFFRECORDTYPE.PROT4REVPASSWORD: - // IsProtected - // break; + // IsProtected + // break; // case BIFFRECORDTYPE.PASSWORD: default: break; diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs index 2f1603ff..857f82a1 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/BinaryFormat/BiffWorkbookReader.cs @@ -9,6 +9,7 @@ internal sealed class BiffWorkbookReader(Stream stream, Dictionary _worksheetRels = worksheetRels; private enum SheetVisibility : byte diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs index c5d932fb..a528df3a 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs @@ -35,7 +35,7 @@ public IEnumerable ReadWorksheets() private void ReadWorkbook() { using RecordReader reader = _zipWorker.GetWorkbookReader(); - + Record record; while ((record = reader.Read()) != null) { From 3ab7633134c53b790d453eff8c2b220b7d4a0854 Mon Sep 17 00:00:00 2001 From: John Opincar Date: Fri, 23 Aug 2024 15:47:01 -0500 Subject: [PATCH 4/8] One more formatting fix; --- src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs index a528df3a..a96f5072 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XlsxWorkbook.cs @@ -35,7 +35,7 @@ public IEnumerable ReadWorksheets() private void ReadWorkbook() { using RecordReader reader = _zipWorker.GetWorkbookReader(); - + Record record; while ((record = reader.Read()) != null) { From 1a6a9b502983afccee8bdc3c2f541aee11bd5f75 Mon Sep 17 00:00:00 2001 From: John Opincar Date: Fri, 23 Aug 2024 17:05:52 -0500 Subject: [PATCH 5/8] Fixes #642: address code analysis issues; --- src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs | 8 ++++---- .../Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs b/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs index 835f3b27..65141453 100644 --- a/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs +++ b/src/ExcelDataReader/Core/CsvFormat/CsvWorksheet.cs @@ -34,16 +34,16 @@ 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; public string VisibleState => null; - public int ActiveSheet => 0; - - public bool IsActiveSheet => true; - public HeaderFooter HeaderFooter => null; public CellRange[] MergeCells => null; diff --git a/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs index 7a0a5078..5ddb3a93 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs @@ -78,9 +78,8 @@ protected override IEnumerable ReadOverride() if (Reader.IsStartElement("workbookView", ProperNamespaces.NsSpreadsheetMl)) { string activeTab = Reader.GetAttribute("activeTab"); - int result = -1; - int.TryParse(activeTab, out result); - yield return new WorkbookActRecord(result); + int activeTabInt = int.TryParse(activeTab, out var result) ? result : -1; + yield return new WorkbookActRecord(activeTabInt); Reader.Skip(); } else if (!XmlReaderHelper.SkipContent(Reader)) From caefc1022475b5df0adee4495fe848ad3b2e9d08 Mon Sep 17 00:00:00 2001 From: John Opincar Date: Mon, 26 Aug 2024 10:43:59 -0500 Subject: [PATCH 6/8] Fixes #642: revert test file changes; --- src/TestData/Test10x10.xls | Bin 13824 -> 13824 bytes src/TestData/TestOpen.xls | Bin 39424 -> 39424 bytes src/TestData/TestUnicodeChars.xls | Bin 14336 -> 14336 bytes src/TestData/Test_Excel_Dataset.xls | Bin 34816 -> 34816 bytes src/TestData/Test_Issue_11570_Excel2013.xls | Bin 25600 -> 25600 bytes src/TestData/Test_OpenOffice.xls | Bin 12800 -> 12800 bytes src/TestData/Test_git_issue_45.xls | Bin 42496 -> 42496 bytes src/TestData/roo_1900_base.xls | Bin 6656 -> 6656 bytes src/TestData/roo_1904_base.xls | Bin 19456 -> 19456 bytes 9 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/TestData/Test10x10.xls b/src/TestData/Test10x10.xls index 1aaaa438831c259316089e8fb5d3a81a3d68775e..cb1ea6ebc081dd4d0f75845bbdc6a3ca3d65b7a6 100644 GIT binary patch delta 38 ucmZq3X~@~2!NkGJzz~$5l$kf#kSTrhbf&9@>>qSKC;f9QYP1gB++1wZ%LoAQR1HP| diff --git a/src/TestData/TestOpen.xls b/src/TestData/TestOpen.xls index c7723825a6e06693fbc365ac5fd83135318b878e..1a01d9746794fb82064affef948d10b4f1d5b4e9 100644 GIT binary patch delta 28 kcmZqJ!ql*ZX@dq63qJ#c*Jd3ihiQ}Vns96`nbE@t0D-d!Qvd(} delta 33 pcmZqJ!ql*ZX@dq63nK%A<7OQuhiM!i>iF(z9lFW5xnxEUBLKD-3%39O diff --git a/src/TestData/TestUnicodeChars.xls b/src/TestData/TestUnicodeChars.xls index 8890b6946854b35eb9757b80fe39a7215b54ed0b..a5b4c5dad9cabf88040b3976bd76c6b6e37878ec 100644 GIT binary patch delta 38 ucmZoDXeijA!NkGJzz~$5l$kf#kSTrhbf&Au>>qUa>JLX6xBX-b91p-FCzf`LJmRz diff --git a/src/TestData/Test_Excel_Dataset.xls b/src/TestData/Test_Excel_Dataset.xls index 09e0f6c246a846fa7e626407eada7150234bcfbc..10fb6aa4b8d035c5ee962cd4b7cc6dc7f7502e70 100644 GIT binary patch delta 32 ocmZpez|=5-X+sVR`-Ru*J3swDzqy!YDug9lE)>IH{Kr0I=cby+wzm+18{0|2fd3Q_<7 delta 38 ucmZojX-L_i!N$(Wz~GpelRVjwEq(JewrlDf1$m4Yv<}^5++3p9!wdlJ%?$nk diff --git a/src/TestData/Test_git_issue_45.xls b/src/TestData/Test_git_issue_45.xls index 354bc20f2418dc5584741f1604eea90a3143e267..1f9798ec0ea3baa3a561db959ae6d2c2d9bca1ce 100644 GIT binary patch delta 31 ncmZoT!_;twX@dq63qJ#c*Jd3i$3<+f7cJpsy|lS_NiQP+rYQ>x delta 31 ncmZoT!_;twX@dq63nK%A<7OQu$3<*~ZF{v2-P~Nfq?Zu@r6~(o diff --git a/src/TestData/roo_1900_base.xls b/src/TestData/roo_1900_base.xls index 931e3e28384b3b600e4a77803552c5640bfca02f..f2b1cc8ff924af86a4545d2eca05e272eeacd8a9 100644 GIT binary patch delta 33 pcmZoLX)xKK!NJbLz~G#ilRVjwBYpEUj%&P=by+wzmx%Q+0|23-3B~{b delta 38 ucmZoLX)xKK!NJbRz~GpelRVjwBYpEUj%&Oe8@duXwGZ87+*~5o!wdl5{tRdU diff --git a/src/TestData/roo_1904_base.xls b/src/TestData/roo_1904_base.xls index b9c4ee5fc5a4cb9a4bc534ad600a0a074105c9c8..a9555536b63210db051e47fcfead84a86dd0728f 100644 GIT binary patch delta 40 wcmZpe!Pqc^af1dM2P*?ZVqQvR`D8=3^v%=Rt~#+Vvk;C01#vk00000 delta 40 wcmZpe!Pqc^af1dM2O|T6V_r&S`D8=3^v%=Rt~# Date: Mon, 26 Aug 2024 14:34:37 -0500 Subject: [PATCH 7/8] Fixes #642: include unit tests from original fork (not sure how they were missed). --- .../ExcelBinaryReaderTest.cs | 24 ++++++++++++++++++ .../ExcelCsvReaderTest.cs | 13 ++++++++++ .../ExcelOpenXmlBinaryReaderTest.cs | 25 +++++++++++++++++++ .../ExcelOpenXmlReaderTest.cs | 24 ++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs b/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs index ca2e345b..8997181b 100644 --- a/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs @@ -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..ea512a91 100644 --- a/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs @@ -467,4 +467,17 @@ 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..cfedca77 100644 --- a/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs @@ -14,6 +14,31 @@ 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); From 410887761777281877610c0066d0be2c13ef2175 Mon Sep 17 00:00:00 2001 From: John Opincar Date: Mon, 26 Aug 2024 16:01:42 -0500 Subject: [PATCH 8/8] Fix failing test; Address style issues; --- src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs | 10 +++++----- src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs | 3 +-- .../ExcelOpenXmlBinaryReaderTest.cs | 3 +-- src/ExcelDataReader.Tests/ExcelTestBase.cs | 2 +- .../Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs | 2 +- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs b/src/ExcelDataReader.Tests/ExcelBinaryReaderTest.cs index 8997181b..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() diff --git a/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs b/src/ExcelDataReader.Tests/ExcelCsvReaderTest.cs index ea512a91..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 { @@ -471,7 +471,6 @@ public void GitIssue463() [Test] public void GitIssue642_ActiveSheet() { - using var reader = ExcelReaderFactory.CreateCsvReader(Configuration.GetTestWorkbook("csv\\MOCK_DATA.csv")); var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration() { diff --git a/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs b/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs index cfedca77..06e15562 100644 --- a/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs +++ b/src/ExcelDataReader.Tests/ExcelOpenXmlBinaryReaderTest.cs @@ -21,8 +21,7 @@ public void GitIssue642_ActiveSheet() 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")); } 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/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs index 5ddb3a93..c10a4ff4 100644 --- a/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs +++ b/src/ExcelDataReader/Core/OpenXmlFormat/XmlFormat/XmlWorkbookReader.cs @@ -78,7 +78,7 @@ protected override IEnumerable ReadOverride() if (Reader.IsStartElement("workbookView", ProperNamespaces.NsSpreadsheetMl)) { string activeTab = Reader.GetAttribute("activeTab"); - int activeTabInt = int.TryParse(activeTab, out var result) ? result : -1; + int activeTabInt = int.TryParse(activeTab, out var result) ? result : 0; yield return new WorkbookActRecord(activeTabInt); Reader.Skip(); }