NanoXLSX.Reader 3.0.0-rc.5
Loading...
Searching...
No Matches
WorksheetReader.cs
1/*
2 * NanoXLSX is a small .NET library to generate and read XLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3 * Copyright Raphael Stoeckli © 2026
4 * This library is licensed under the MIT License.
5 * You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6 */
7
8using System;
9using System.Collections.Generic;
10using System.Globalization;
11using System.IO;
12using System.Linq;
13using System.Xml;
14using NanoXLSX.Exceptions;
15using NanoXLSX.Interfaces;
16using NanoXLSX.Interfaces.Reader;
17using NanoXLSX.Registry;
18using NanoXLSX.Styles;
19using NanoXLSX.Utils;
20using static NanoXLSX.Enums.Password;
21using IOException = NanoXLSX.Exceptions.IOException;
22
24{
28 public class WorksheetReader : IWorksheetReader
29 {
30 #region privateFields
31 private MemoryStream stream;
32 private List<string> dateStyles;
33 private List<string> timeStyles;
34 private Dictionary<string, Style> resolvedStyles;
35 private IPasswordReader passwordReader;
36 #endregion
37
38 #region properties
39
43 public Workbook Workbook { get; set; }
47 public IOptions Options { get; set; }
51 public Action<MemoryStream, Workbook, string, IOptions, int?> InlinePluginHandler { get; set; }
55 public int CurrentWorksheetID { get; set; }
56
60 public List<String> SharedStrings { get; set; }
61 #endregion
62
63 #region constructors
68 {
69 }
70 #endregion
71
72 #region functions
80 public void Init(MemoryStream stream, Workbook workbook, IOptions readerOptions, Action<MemoryStream, Workbook, string, IOptions, int?> inlinePluginHandler)
81 {
82 this.stream = stream;
83 this.Workbook = workbook;
84 this.Options = readerOptions;
85 this.InlinePluginHandler = inlinePluginHandler;
86 //this.readerOptions = readerOptions as ReaderOptions;
87 if (dateStyles == null || timeStyles == null || this.resolvedStyles == null)
88 {
89 StyleReaderContainer styleReaderContainer = workbook.AuxiliaryData.GetData<StyleReaderContainer>(PlugInUUID.StyleReader, PlugInUUID.StyleEntity);
90 ProcessStyles(styleReaderContainer);
91 }
92 if (this.passwordReader == null)
93 {
94 this.passwordReader = PlugInLoader.GetPlugIn<IPasswordReader>(PlugInUUID.PasswordReader, new LegacyPasswordReader());
95 this.passwordReader.Init(PasswordType.WorksheetProtection, (ReaderOptions)readerOptions);
96 }
97 }
98
103 public void Execute()
104 {
105 try
106 {
107 WorksheetDefinition worksheetDefinition = Workbook.AuxiliaryData.GetData<WorksheetDefinition>(PlugInUUID.WorkbookReader, PlugInUUID.WorksheetDefinitionEntity, CurrentWorksheetID);
108 Worksheet worksheet = new Worksheet(worksheetDefinition.WorksheetName, CurrentWorksheetID, Workbook)
109 {
110 Hidden = worksheetDefinition.Hidden
111 };
112 using (stream) // Close after processing
113 {
114 ReaderOptions readerOptions = this.Options as ReaderOptions;
115 XmlDocument document = new XmlDocument() { XmlResolver = null };
116 using (XmlReader reader = XmlReader.Create(stream, new XmlReaderSettings() { XmlResolver = null }))
117 {
118 document.Load(reader);
119 GetRows(document, worksheet, readerOptions);
120 GetSheetView(document, worksheet);
121 GetMergedCells(document, worksheet);
122 GetSheetFormats(document, worksheet);
123 GetAutoFilters(document, worksheet);
124 GetColumns(document, worksheet, readerOptions);
125 GetSheetProtection(document, worksheet);
126 SetWorkbookRelation(worksheet);
127 InlinePluginHandler?.Invoke(stream, Workbook, PlugInUUID.WorksheetInlineReader, Options, CurrentWorksheetID);
128 }
129 }
130 }
131 catch (NotSupportedContentException)
132 {
133 throw; // rethrow
134 }
135 catch (Exception ex)
136 {
137 throw new IOException("The XML entry could not be read from the input stream. Please see the inner exception:", ex);
138 }
139 }
140
145 private void SetWorkbookRelation(Worksheet worksheet)
146 {
147 Workbook.AddWorksheet(worksheet);
148 int selectedWorksheetId = Workbook.AuxiliaryData.GetData<int>(PlugInUUID.WorkbookReader, PlugInUUID.SelectedWorksheetEntity);
149 if (selectedWorksheetId + 1 == CurrentWorksheetID) // selectedWorksheetId is 0-based
150 {
151 Workbook.SetSelectedWorksheet(worksheet);
152 }
153 }
154
159 private void ProcessStyles(StyleReaderContainer styleReaderContainer)
160 {
161 this.dateStyles = new List<string>();
162 this.timeStyles = new List<string>();
163 this.resolvedStyles = new Dictionary<string, Style>();
164 for (int i = 0; i < styleReaderContainer.StyleCount; i++)
165 {
166 bool isDate;
167 bool isTime;
168 string index = ParserUtils.ToString(i);
169 Style style = styleReaderContainer.GetStyle(i, out isDate, out isTime);
170 if (isDate)
171 {
172 this.dateStyles.Add(index);
173 }
174 if (isTime)
175 {
176 this.timeStyles.Add(index);
177 }
178 this.resolvedStyles.Add(index, style);
179 }
180 }
181
188 private void GetRows(XmlDocument document, Worksheet worksheet, ReaderOptions readerOptions)
189 {
190 XmlNodeList rows = document.GetElementsByTagName("row");
191 foreach (XmlNode row in rows)
192 {
193 string rowAttribute = ReaderUtils.GetAttribute(row, "r");
194 if (rowAttribute != null)
195 {
196 int rowNumber = ParserUtils.ParseInt(rowAttribute) - 1; // Transform to zero-based
197 string hiddenAttribute = ReaderUtils.GetAttribute(row, "hidden");
198 if (hiddenAttribute != null)
199 {
200 int value = ParserUtils.ParseBinaryBool(hiddenAttribute);
201 if (value == 1)
202 {
203 worksheet.AddHiddenRow(rowNumber);
204 }
205 }
206 string heightAttribute = ReaderUtils.GetAttribute(row, "ht");
207 if (heightAttribute != null)
208 {
209 worksheet.RowHeights.Add(rowNumber, GetValidatedHeight(ParserUtils.ParseFloat(heightAttribute), readerOptions));
210 }
211 }
212 if (row.HasChildNodes)
213 {
214 foreach (XmlNode rowChild in row.ChildNodes)
215 {
216 ReadCell(rowChild, worksheet);
217 }
218 }
219 }
220 }
221
227 private static void GetSheetView(XmlDocument xmlDocument, Worksheet worksheet)
228 {
229 XmlNodeList sheetViewsNodes = xmlDocument.GetElementsByTagName("sheetViews");
230 if (sheetViewsNodes != null && sheetViewsNodes.Count > 0)
231 {
232 XmlNodeList sheetViewNodes = sheetViewsNodes[0].ChildNodes;
233 string attribute;
234 // Go through all possible views
235 foreach (XmlNode sheetView in sheetViewNodes)
236 {
237 attribute = ReaderUtils.GetAttribute(sheetView, "view", string.Empty);
238 worksheet.ViewType = Worksheet.GetSheetViewTypeEnum(attribute);
239 attribute = ReaderUtils.GetAttribute(sheetView, "zoomScale");
240 if (attribute != null)
241 {
242 worksheet.ZoomFactor = ParserUtils.ParseInt(attribute);
243 }
244 attribute = ReaderUtils.GetAttribute(sheetView, "zoomScaleNormal");
245 if (attribute != null)
246 {
247 int scale = ParserUtils.ParseInt(attribute);
248 worksheet.ZoomFactors[Worksheet.SheetViewType.Normal] = scale;
249 }
250 attribute = ReaderUtils.GetAttribute(sheetView, "zoomScalePageLayoutView");
251 if (attribute != null)
252 {
253 int scale = ParserUtils.ParseInt(attribute);
254 worksheet.ZoomFactors[Worksheet.SheetViewType.PageLayout] = scale;
255 }
256 attribute = ReaderUtils.GetAttribute(sheetView, "zoomScaleSheetLayoutView");
257 if (attribute != null)
258 {
259 int scale = ParserUtils.ParseInt(attribute);
260 worksheet.ZoomFactors[Worksheet.SheetViewType.PageBreakPreview] = scale;
261 }
262 attribute = ReaderUtils.GetAttribute(sheetView, "showGridLines");
263 if (attribute != null)
264 {
265 worksheet.ShowGridLines = ParserUtils.ParseBinaryBool(attribute) == 1;
266 }
267 attribute = ReaderUtils.GetAttribute(sheetView, "showRowColHeaders");
268 if (attribute != null)
269 {
270 worksheet.ShowRowColumnHeaders = ParserUtils.ParseBinaryBool(attribute) == 1;
271 }
272 attribute = ReaderUtils.GetAttribute(sheetView, "showRuler");
273 if (attribute != null)
274 {
275 worksheet.ShowRuler = ParserUtils.ParseBinaryBool(attribute) == 1;
276 }
277 if (sheetView.LocalName.Equals("sheetView", StringComparison.OrdinalIgnoreCase))
278 {
279 XmlNodeList selectionNodes = sheetView.ChildNodes;
280 if (selectionNodes != null && selectionNodes.Count > 0)
281 {
282 foreach (XmlNode selectionNode in selectionNodes)
283 {
284 attribute = ReaderUtils.GetAttribute(selectionNode, "sqref");
285 if (attribute != null)
286 {
287 if (attribute.Contains(" "))
288 {
289 // Multiple ranges
290 string[] ranges = attribute.Split(' ');
291 foreach (string range in ranges)
292 {
293 CollectSelectedCells(range, worksheet);
294 }
295 }
296 else
297 {
298 CollectSelectedCells(attribute, worksheet);
299 }
300
301 }
302 }
303 }
304 XmlNode paneNode = ReaderUtils.GetChildNode(sheetView, "pane");
305 if (paneNode != null)
306 {
307 SetPaneSplit(paneNode, worksheet);
308 }
309 }
310 }
311 }
312 }
313
319 private static void CollectSelectedCells(string attribute, Worksheet worksheet)
320 {
321 if (attribute.Contains(":"))
322 {
323 // One range
324 worksheet.AddSelectedCells(new Range(attribute));
325 }
326 else
327 {
328 // One cell
329 worksheet.AddSelectedCells(new Range(attribute + ":" + attribute));
330 }
331 }
332
338 private static void SetPaneSplit(XmlNode paneNode, Worksheet worksheet)
339 {
340 string attribute = ReaderUtils.GetAttribute(paneNode, "state");
341 bool useNumbers = false;
342 bool frozenState = false;
343 bool ySplitDefined = false;
344 bool xSplitDefined = false;
345 int? paneSplitRowIndex = null;
346 int? paneSplitColumnIndex = null;
347 float? paneSplitHeight = null;
348 float? paneSplitWidth = null;
349 Address topLeftCell = new Address(0, 0); // default value
350 Worksheet.WorksheetPane? activePane = null;
351 if (attribute != null)
352 {
353 if (ParserUtils.ToLower(attribute) == "frozen" || ParserUtils.ToLower(attribute) == "frozensplit")
354 {
355 frozenState = true;
356 }
357 useNumbers = frozenState;
358 }
359 attribute = ReaderUtils.GetAttribute(paneNode, "ySplit");
360 if (attribute != null)
361 {
362 ySplitDefined = true;
363 if (useNumbers)
364 {
365 paneSplitRowIndex = ParserUtils.ParseInt(attribute);
366 }
367 else
368 {
369 paneSplitHeight = DataUtils.GetPaneSplitHeight(ParserUtils.ParseFloat(attribute));
370 }
371 }
372 attribute = ReaderUtils.GetAttribute(paneNode, "xSplit");
373 if (attribute != null)
374 {
375 xSplitDefined = true;
376 if (useNumbers)
377 {
378 paneSplitColumnIndex = ParserUtils.ParseInt(attribute);
379 }
380 else
381 {
382 paneSplitWidth = DataUtils.GetPaneSplitWidth(ParserUtils.ParseFloat(attribute));
383 }
384 }
385 attribute = ReaderUtils.GetAttribute(paneNode, "topLeftCell");
386 if (attribute != null)
387 {
388 topLeftCell = new Address(attribute);
389 }
390 attribute = ReaderUtils.GetAttribute(paneNode, "activePane", string.Empty);
391 activePane = Worksheet.GetWorksheetPaneEnum(attribute);
392 if (frozenState)
393 {
394 if (ySplitDefined && !xSplitDefined)
395 {
396 worksheet.SetHorizontalSplit(paneSplitRowIndex.Value, frozenState, topLeftCell, activePane);
397 }
398 if (!ySplitDefined && xSplitDefined)
399 {
400 worksheet.SetVerticalSplit(paneSplitColumnIndex.Value, frozenState, topLeftCell, activePane);
401 }
402 else if (ySplitDefined && xSplitDefined)
403 {
404 worksheet.SetSplit(paneSplitColumnIndex.Value, paneSplitRowIndex.Value, frozenState, topLeftCell, activePane);
405 }
406 }
407 else
408 {
409 if (ySplitDefined && !xSplitDefined)
410 {
411 worksheet.SetHorizontalSplit(paneSplitHeight.Value, topLeftCell, activePane);
412 }
413 if (!ySplitDefined && xSplitDefined)
414 {
415 worksheet.SetVerticalSplit(paneSplitWidth.Value, topLeftCell, activePane);
416 }
417 else if (ySplitDefined && xSplitDefined)
418 {
419 worksheet.SetSplit(paneSplitWidth, paneSplitHeight, topLeftCell, activePane);
420 }
421 }
422 }
423
429 private void GetSheetProtection(XmlDocument xmlDocument, Worksheet worksheet)
430 {
431 ReaderOptions readerOptions = this.Options as ReaderOptions;
432 XmlNodeList sheetProtectionNodes = xmlDocument.GetElementsByTagName("sheetProtection");
433 if (sheetProtectionNodes != null && sheetProtectionNodes.Count > 0)
434 {
435 int hasProtection = 0;
436 XmlNode sheetProtectionNode = sheetProtectionNodes[0];
437 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.AutoFilter, worksheet);
438 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.DeleteColumns, worksheet);
439 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.DeleteRows, worksheet);
440 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.FormatCells, worksheet);
441 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.FormatColumns, worksheet);
442 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.FormatRows, worksheet);
443 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.InsertColumns, worksheet);
444 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.InsertHyperlinks, worksheet);
445 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.InsertRows, worksheet);
446 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.Objects, worksheet);
447 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.PivotTables, worksheet);
448 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.Scenarios, worksheet);
449 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.SelectLockedCells, worksheet);
450 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.SelectUnlockedCells, worksheet);
451 hasProtection += ManageSheetProtection(sheetProtectionNode, Worksheet.SheetProtectionValue.Sort, worksheet);
452 if (hasProtection > 0)
453 {
454 worksheet.UseSheetProtection = true;
455 }
456 this.passwordReader.ReadXmlAttributes(sheetProtectionNode);
457 if (this.passwordReader.PasswordIsSet())
458 {
459 if (this.passwordReader is LegacyPasswordReader && (this.passwordReader as LegacyPasswordReader).ContemporaryAlgorithmDetected && (readerOptions == null || !readerOptions.IgnoreNotSupportedPasswordAlgorithms))
460 {
461 throw new NotSupportedContentException("A not supported, contemporary password algorithm for the worksheet protection was detected. Check possible packages to add support to NanoXLSX, or ignore this error by a reader option");
462 }
463 worksheet.SheetProtectionPassword.CopyFrom(this.passwordReader);
464 }
465 }
466 }
467
474 private static int ManageSheetProtection(XmlNode node, Worksheet.SheetProtectionValue sheetProtectionValue, Worksheet worksheet)
475 {
476 int hasProtection = 0;
477 string attributeName = Worksheet.GetSheetProtectionName(sheetProtectionValue);
478 string attribute = ReaderUtils.GetAttribute(node, attributeName);
479 if (attribute != null)
480 {
481 hasProtection = 1;
482 worksheet.SheetProtectionValues.Add(sheetProtectionValue);
483 }
484 return hasProtection;
485 }
486
492 private static void GetMergedCells(XmlDocument xmlDocument, Worksheet worksheet)
493 {
494 XmlNodeList mergedCellsNodes = xmlDocument.GetElementsByTagName("mergeCells");
495 if (mergedCellsNodes != null && mergedCellsNodes.Count > 0)
496 {
497 XmlNodeList mergedCellNodes = mergedCellsNodes[0].ChildNodes;
498 if (mergedCellNodes != null && mergedCellNodes.Count > 0)
499 {
500 foreach (XmlNode mergedCells in mergedCellNodes)
501 {
502 string attribute = ReaderUtils.GetAttribute(mergedCells, "ref");
503 if (attribute != null)
504 {
505 worksheet.MergeCells(new Range(attribute));
506 }
507 }
508 }
509 }
510 }
511
517 private static void GetSheetFormats(XmlDocument xmlDocument, Worksheet worksheet)
518 {
519 XmlNodeList formatNodes = xmlDocument.GetElementsByTagName("sheetFormatPr");
520 if (formatNodes != null && formatNodes.Count > 0)
521 {
522 string attribute = ReaderUtils.GetAttribute(formatNodes[0], "defaultColWidth");
523 if (attribute != null)
524 {
525 worksheet.DefaultColumnWidth = ParserUtils.ParseFloat(attribute);
526 }
527 attribute = ReaderUtils.GetAttribute(formatNodes[0], "defaultRowHeight");
528 if (attribute != null)
529 {
530 worksheet.DefaultRowHeight = ParserUtils.ParseFloat(attribute);
531 }
532 }
533 }
534
540 private static void GetAutoFilters(XmlDocument xmlDocument, Worksheet worksheet)
541 {
542 XmlNodeList autoFilterNodes = xmlDocument.GetElementsByTagName("autoFilter");
543 if (autoFilterNodes != null && autoFilterNodes.Count > 0)
544 {
545 string autoFilterRef = ReaderUtils.GetAttribute(autoFilterNodes[0], "ref");
546 if (autoFilterRef != null)
547 {
548 Range range = new Range(autoFilterRef);
549 worksheet.SetAutoFilter(range.StartAddress.Column, range.EndAddress.Column);
550 }
551 }
552 }
553
560 private void GetColumns(XmlDocument xmlDocument, Worksheet worksheet, ReaderOptions readerOptions)
561 {
562 XmlNodeList columnNodes = xmlDocument.GetElementsByTagName("col");
563 foreach (XmlNode columnNode in columnNodes)
564 {
565 int? min = null;
566 int? max = null;
567 List<int> indices = new List<int>();
568 string attribute = ReaderUtils.GetAttribute(columnNode, "min");
569 if (attribute != null)
570 {
571 min = ParserUtils.ParseInt(attribute);
572 max = min;
573 indices.Add(min.Value);
574 }
575 attribute = ReaderUtils.GetAttribute(columnNode, "max");
576 if (attribute != null)
577 {
578 max = ParserUtils.ParseInt(attribute);
579 }
580 if (min != null && max.Value != min.Value)
581 {
582 for (int i = min.Value; i <= max.Value; i++)
583 {
584 indices.Add(i);
585 }
586 }
587 attribute = ReaderUtils.GetAttribute(columnNode, "width");
588 float width = Worksheet.DefaultWorksheetColumnWidth;
589 if (attribute != null)
590 {
591 width = ParserUtils.ParseFloat(attribute);
592 }
593 attribute = ReaderUtils.GetAttribute(columnNode, "hidden");
594 bool hidden = false;
595 if (attribute != null)
596 {
597 int value = ParserUtils.ParseBinaryBool(attribute);
598 if (value == 1)
599 {
600 hidden = true;
601 }
602 }
603 attribute = ReaderUtils.GetAttribute(columnNode, "style");
604 Style defaultStyle = null;
605 if (attribute != null && resolvedStyles.TryGetValue(attribute, out var attributeValue))
606 {
607 defaultStyle = attributeValue;
608 }
609 foreach (int index in indices)
610 {
611 string columnAddress = Cell.ResolveColumnAddress(index - 1); // Transform to zero-based
612 if (defaultStyle != null)
613 {
614 worksheet.SetColumnDefaultStyle(columnAddress, defaultStyle);
615 }
616
617 if (width != Worksheet.DefaultWorksheetColumnWidth)
618 {
619 worksheet.SetColumnWidth(columnAddress, GetValidatedWidth(width, readerOptions));
620 }
621 if (hidden)
622 {
623 worksheet.AddHiddenColumn(columnAddress);
624 }
625 }
626 }
627 }
628
634 private void ReadCell(XmlNode rowChild, Worksheet worksheet)
635 {
636 string type = "s";
637 string styleNumber = "";
638 string address = "A1";
639 string value = "";
640 if (rowChild.LocalName.Equals("c", StringComparison.OrdinalIgnoreCase))
641 {
642 address = ReaderUtils.GetAttribute(rowChild, "r"); // Mandatory
643 type = ReaderUtils.GetAttribute(rowChild, "t"); // can be null if not existing
644 styleNumber = ReaderUtils.GetAttribute(rowChild, "s"); // can be null
645 if (rowChild.HasChildNodes)
646 {
647 foreach (XmlNode valueNode in rowChild.ChildNodes)
648 {
649 if (valueNode.LocalName.Equals("v", StringComparison.OrdinalIgnoreCase))
650 {
651 value = valueNode.InnerText;
652 }
653 if (valueNode.LocalName.Equals("f", StringComparison.OrdinalIgnoreCase))
654 {
655 value = valueNode.InnerText;
656 }
657 if (valueNode.LocalName.Equals("is", StringComparison.OrdinalIgnoreCase))
658 {
659 value = valueNode.InnerText;
660 }
661 }
662 }
663 }
664 string key = ParserUtils.ToUpper(address);
665 Cell cell = ResolveCellData(value, type, styleNumber, address);
666 worksheet.AddCell(cell, address);
667 if (styleNumber != null)
668 {
669 Style style = null;
670 this.resolvedStyles.TryGetValue(styleNumber, out style);
671 if (style != null)
672 {
673 worksheet.Cells[address].SetStyle(style);
674 }
675 }
676 }
677
686 private Cell ResolveCellData(string raw, string type, string styleNumber, string address)
687 {
688 ReaderOptions readerOptions = this.Options as ReaderOptions;
689 Cell.CellType importedType = Cell.CellType.Default;
690 object rawValue;
691 if (type == "b")
692 {
693 rawValue = TryParseBool(raw);
694 if (rawValue != null)
695 {
696 importedType = Cell.CellType.Bool;
697 }
698 else
699 {
700 rawValue = GetNumericValue(raw);
701 if (rawValue != null)
702 {
703 importedType = Cell.CellType.Number;
704 }
705 }
706 }
707 else if (type == "s")
708 {
709 importedType = Cell.CellType.String;
710 rawValue = ResolveSharedString(raw);
711 }
712 else if (type == "str")
713 {
714 importedType = Cell.CellType.Formula;
715 rawValue = raw;
716 }
717 else if (type == "inlineStr")
718 {
719 importedType = Cell.CellType.String;
720 rawValue = raw;
721 }
722 else if (dateStyles.Contains(styleNumber) && (type == null || type == "" || type == "n"))
723 {
724 rawValue = GetDateTimeValue(raw, Cell.CellType.Date, out importedType);
725 }
726 else if (timeStyles.Contains(styleNumber) && (type == null || type == "" || type == "n"))
727 {
728 rawValue = GetDateTimeValue(raw, Cell.CellType.Time, out importedType);
729 }
730 else
731 {
732 importedType = Cell.CellType.Number;
733 rawValue = GetNumericValue(raw);
734 }
735 if (rawValue == null && raw == "")
736 {
737 importedType = Cell.CellType.Empty;
738 rawValue = null;
739 }
740 else if (rawValue == null && raw.Length > 0)
741 {
742 importedType = Cell.CellType.String;
743 rawValue = raw;
744 }
745 Address cellAddress = new Address(address);
746 if (readerOptions != null)
747 {
748 if (readerOptions.EnforcedColumnTypes.Count > 0)
749 {
750 rawValue = GetEnforcedColumnValue(rawValue, importedType, cellAddress);
751 }
752 rawValue = GetGloballyEnforcedValue(rawValue, cellAddress);
753 rawValue = GetGloballyEnforcedFlagValues(rawValue, cellAddress);
754 importedType = ResolveType(rawValue, importedType);
755 if (importedType == Cell.CellType.Date && rawValue is DateTime && (DateTime)rawValue < DataUtils.FirstAllowedExcelDate)
756 {
757 // Fix conversion from time to date, where time has no days
758 rawValue = ((DateTime)rawValue).AddDays(1);
759 }
760 }
761 return CreateCell(rawValue, importedType, cellAddress, styleNumber);
762 }
763
770 private static Cell.CellType ResolveType(object value, Cell.CellType defaultType)
771 {
772 if (defaultType == Cell.CellType.Formula)
773 {
774 return defaultType;
775 }
776 if (value == null)
777 {
778 return Cell.CellType.Empty;
779 }
780 switch (value)
781 {
782 case uint _:
783 case long _:
784 case ulong _:
785 case short _:
786 case ushort _:
787 case float _:
788 case double _:
789 case byte _:
790 case sbyte _:
791 case int _:
792 return Cell.CellType.Number;
793 case DateTime _:
794 return Cell.CellType.Date;
795 case TimeSpan _:
796 return Cell.CellType.Time;
797 case bool _:
798 return Cell.CellType.Bool;
799 default:
800 return Cell.CellType.String;
801 }
802 }
803
810 private object GetGloballyEnforcedFlagValues(object data, Address address)
811 {
812 ReaderOptions readerOptions = this.Options as ReaderOptions;
813 if (address.Row < readerOptions.EnforcingStartRowNumber)
814 {
815 return data;
816 }
817 if (readerOptions.EnforceDateTimesAsNumbers)
818 {
819 if (data is DateTime)
820 {
821 data = DataUtils.GetOADateTime((DateTime)data, true);
822 }
823 else if (data is TimeSpan)
824 {
825 data = DataUtils.GetOATime((TimeSpan)data);
826 }
827 }
828 if (readerOptions.EnforceEmptyValuesAsString && data == null)
829 {
830 return "";
831 }
832 return data;
833 }
834
841 private object GetGloballyEnforcedValue(object data, Address address)
842 {
843 ReaderOptions readerOptions = this.Options as ReaderOptions;
844 if (address.Row < readerOptions.EnforcingStartRowNumber)
845 {
846 return data;
847 }
848 if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToDouble)
849 {
850 object tempDouble = ConvertToDouble(data, readerOptions);
851 if (tempDouble != null)
852 {
853 return tempDouble;
854 }
855 }
856 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToDecimal)
857 {
858 object tempDecimal = ConvertToDecimal(data, readerOptions);
859 if (tempDecimal != null)
860 {
861 return tempDecimal;
862 }
863 }
864 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToInt)
865 {
866 object tempInt = ConvertToInt(data);
867 if (tempInt != null)
868 {
869 return tempInt;
870 }
871 }
872 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.EverythingToString)
873 {
874 return ConvertToString(data, readerOptions);
875 }
876 return data;
877 }
878
886 private object GetEnforcedColumnValue(object data, Cell.CellType importedTyp, Address address)
887 {
888 ReaderOptions readerOptions = this.Options as ReaderOptions;
889 if (address.Row < readerOptions.EnforcingStartRowNumber)
890 {
891 return data;
892 }
893 if (!readerOptions.EnforcedColumnTypes.TryGetValue(address.Column, out var columnType))
894 {
895 return data;
896 }
897 if (importedTyp == Cell.CellType.Formula)
898 {
899 return data;
900 }
901 switch (columnType)
902 {
903 case ReaderOptions.ColumnType.Numeric:
904 return GetNumericValue(data, importedTyp, readerOptions);
905 case ReaderOptions.ColumnType.Decimal:
906 return ConvertToDecimal(data, readerOptions);
907 case ReaderOptions.ColumnType.Double:
908 return ConvertToDouble(data, readerOptions);
909 case ReaderOptions.ColumnType.Date:
910 return ConvertToDate(data, readerOptions);
911 case ReaderOptions.ColumnType.Time:
912 return ConvertToTime(data, readerOptions);
913 case ReaderOptions.ColumnType.Bool:
914 return ConvertToBool(data, readerOptions);
915 default:
916 return ConvertToString(data, readerOptions);
917 }
918 }
919
926 private object ConvertToBool(object data, ReaderOptions readerOptions)
927 {
928 switch (data)
929 {
930 case bool _:
931 return data;
932 case uint _:
933 case long _:
934 case ulong _:
935 case short _:
936 case ushort _:
937 case float _:
938 case byte _:
939 case sbyte _:
940 case int _:
941 object tempObject = ConvertToDouble(data, readerOptions);
942 if (tempObject is double)
943 {
944 double tempDouble = (double)tempObject;
945 if (double.Equals(tempDouble, 0d))
946 {
947 return false;
948 }
949 else if (double.Equals(tempDouble, 1d))
950 {
951 return true;
952 }
953 }
954 break;
955 case string _:
956
957 string tempString = (string)data;
958 bool? tempBool = TryParseBool(tempString);
959 if (tempBool != null)
960 {
961 return tempBool.Value;
962 }
963 break;
964 }
965 return data;
966 }
967
973 private static bool? TryParseBool(string raw)
974 {
975 if (raw == "0")
976 {
977 return false;
978 }
979 else if (raw == "1")
980 {
981 return true;
982 }
983 else
984 {
985 bool value;
986 if (bool.TryParse(raw, out value))
987 {
988 return value;
989 }
990 else
991 {
992 return null;
993 }
994 }
995 }
996
1003 private object ConvertToDouble(object data, ReaderOptions readerOptions)
1004 {
1005 object value = ConvertToDecimal(data, readerOptions);
1006 if (value is decimal)
1007 {
1008 return Decimal.ToDouble((decimal)value);
1009 }
1010 else if (value is float)
1011 {
1012 return Convert.ToDouble((float)value);
1013 }
1014 return value;
1015 }
1016
1023 private object ConvertToDecimal(object data, ReaderOptions readerOptions)
1024 {
1025 IConvertible converter;
1026 switch (data)
1027 {
1028 case double _:
1029 return data;
1030 case uint _:
1031 case long _:
1032 case ulong _:
1033 case short _:
1034 case ushort _:
1035 case float _:
1036 case byte _:
1037 case sbyte _:
1038 case int _:
1039 converter = data as IConvertible;
1040 double tempDouble = converter.ToDouble(DataUtils.InvariantCulture);
1041 if (tempDouble > (double)decimal.MaxValue || tempDouble < (double)decimal.MinValue)
1042 {
1043 return data;
1044 }
1045 else
1046 {
1047 return converter.ToDecimal(DataUtils.InvariantCulture);
1048 }
1049 case bool _:
1050 if ((bool)data)
1051 {
1052 return decimal.One;
1053 }
1054 else
1055 {
1056 return decimal.Zero;
1057 }
1058 case DateTime _:
1059 return new decimal(DataUtils.GetOADateTime((DateTime)data));
1060 case TimeSpan _:
1061 return new decimal(DataUtils.GetOATime((TimeSpan)data));
1062 case string _:
1063 decimal dValue;
1064 string tempString = (string)data;
1065 if (ParserUtils.TryParseDecimal(tempString, out dValue))
1066 {
1067 return dValue;
1068 }
1069 DateTime? tempDate = TryParseDate(tempString, readerOptions);
1070 if (tempDate != null)
1071 {
1072 return new decimal(DataUtils.GetOADateTime(tempDate.Value));
1073 }
1074 TimeSpan? tempTime = TryParseTime(tempString, readerOptions);
1075 if (tempTime != null)
1076 {
1077 return new decimal(DataUtils.GetOATime(tempTime.Value));
1078 }
1079 break;
1080 }
1081 return data;
1082 }
1083
1089 private static object ConvertToInt(object data)
1090 {
1091 double tempDouble;
1092 switch (data)
1093 {
1094 case uint _:
1095 case long _:
1096 case ulong _:
1097 break;
1098 case DateTime _:
1099 tempDouble = DataUtils.GetOADateTime((DateTime)data, true);
1100 return ConvertDoubleToInt(tempDouble);
1101 case TimeSpan _:
1102 tempDouble = DataUtils.GetOATime((TimeSpan)data);
1103 return ConvertDoubleToInt(tempDouble);
1104 case float _:
1105 case double _:
1106 int? tempInt = TryConvertDoubleToInt(data);
1107 if (tempInt != null)
1108 {
1109 return tempInt;
1110 }
1111 break;
1112 case bool _:
1113 return (bool)data ? 1 : 0;
1114 case string _:
1115 int tempInt2;
1116 if (ParserUtils.TryParseInt((string)data, out tempInt2))
1117 {
1118 return tempInt2;
1119 }
1120 break;
1121 }
1122 return null;
1123 }
1124
1131 private object ConvertToDate(object data, ReaderOptions readerOptions)
1132 {
1133 switch (data)
1134 {
1135 case DateTime _:
1136 return data;
1137 case TimeSpan _:
1138 DateTime root = DataUtils.FirstAllowedExcelDate;
1139 TimeSpan time = (TimeSpan)data;
1140 root = root.AddDays(-1); // Fix offset of 1
1141 root = root.AddHours(time.Hours);
1142 root = root.AddMinutes(time.Minutes);
1143 root = root.AddSeconds(time.Seconds);
1144 return root;
1145 case double _:
1146 case uint _:
1147 case long _:
1148 case ulong _:
1149 case short _:
1150 case ushort _:
1151 case float _:
1152 case byte _:
1153 case sbyte _:
1154 case int _:
1155 return ConvertDateFromDouble(data, readerOptions);
1156 case string _:
1157 DateTime? date2 = TryParseDate((string)data, readerOptions);
1158 if (date2 != null)
1159 {
1160 return date2.Value;
1161 }
1162 return ConvertDateFromDouble(data, readerOptions);
1163 }
1164 return data;
1165 }
1166
1173 private DateTime? TryParseDate(string raw, ReaderOptions readerOptions)
1174 {
1175 DateTime dateTime;
1176 bool isDateTime;
1177 if (readerOptions == null || string.IsNullOrEmpty(readerOptions.DateTimeFormat) || readerOptions.TemporalCultureInfo == null)
1178 {
1179 isDateTime = DateTime.TryParse(raw, ReaderOptions.DefaultCultureInfo, DateTimeStyles.None, out dateTime);
1180 }
1181 else
1182 {
1183 isDateTime = DateTime.TryParseExact(raw, readerOptions.DateTimeFormat, readerOptions.TemporalCultureInfo, DateTimeStyles.None, out dateTime);
1184 }
1185 if (isDateTime && dateTime >= DataUtils.FirstAllowedExcelDate && dateTime <= DataUtils.LastAllowedExcelDate)
1186 {
1187 return dateTime;
1188 }
1189 return null;
1190 }
1191
1198 private object ConvertToTime(object data, ReaderOptions readerOptions)
1199 {
1200 switch (data)
1201 {
1202 case DateTime _:
1203 return ConvertTimeFromDouble(data, readerOptions);
1204 case TimeSpan _:
1205 return data;
1206 case double _:
1207 case uint _:
1208 case long _:
1209 case ulong _:
1210 case short _:
1211 case ushort _:
1212 case float _:
1213 case byte _:
1214 case sbyte _:
1215 case int _:
1216 return ConvertTimeFromDouble(data, readerOptions);
1217 case string _:
1218 TimeSpan? time = TryParseTime((string)data, readerOptions);
1219 if (time != null)
1220 {
1221 return time;
1222 }
1223 return ConvertTimeFromDouble(data, readerOptions);
1224 }
1225 return data;
1226 }
1227
1234 private TimeSpan? TryParseTime(string raw, ReaderOptions readerOptions)
1235 {
1236 TimeSpan timeSpan;
1237 bool isTimeSpan;
1238 if (readerOptions == null || string.IsNullOrEmpty(readerOptions.TimeSpanFormat) || readerOptions.TemporalCultureInfo == null)
1239 {
1240 isTimeSpan = TimeSpan.TryParse(raw, ReaderOptions.DefaultCultureInfo, out timeSpan);
1241 }
1242 else
1243 {
1244 isTimeSpan = TimeSpan.TryParseExact(raw, readerOptions.TimeSpanFormat, readerOptions.TemporalCultureInfo, out timeSpan);
1245 }
1246 if (isTimeSpan && timeSpan.Days >= 0 && timeSpan.Days < DataUtils.MaxOADateValue)
1247 {
1248 return timeSpan;
1249 }
1250 return null;
1251 }
1252
1261 private static object GetDateTimeValue(string raw, Cell.CellType valueType, out Cell.CellType resolvedType)
1262 {
1263 double dValue;
1264 if (!ParserUtils.TryParseDouble(raw, out dValue))
1265 {
1266 resolvedType = Cell.CellType.String;
1267 return raw;
1268 }
1269 if ((valueType == Cell.CellType.Date && (dValue < DataUtils.MinOADateValue || dValue > DataUtils.MaxOADateValue)) || (valueType == Cell.CellType.Time && (dValue < 0.0 || dValue > DataUtils.MaxOADateValue)))
1270 {
1271 // fallback to number (cannot be anything else)
1272 resolvedType = Cell.CellType.Number;
1273 return GetNumericValue(raw);
1274 }
1275 DateTime tempDate = DataUtils.GetDateFromOA(dValue);
1276 if (dValue < 1.0)
1277 {
1278 tempDate = tempDate.AddDays(1); // Modify wrong 1st date when < 1
1279 }
1280 if (valueType == Cell.CellType.Date)
1281 {
1282 resolvedType = Cell.CellType.Date;
1283 return tempDate;
1284 }
1285 else
1286 {
1287 resolvedType = Cell.CellType.Time;
1288 return new TimeSpan((int)dValue, tempDate.Hour, tempDate.Minute, tempDate.Second);
1289 }
1290 }
1291
1298 private object ConvertDateFromDouble(object data, ReaderOptions readerOptions)
1299 {
1300 object oaDate = ConvertToDouble(data, readerOptions);
1301 if (oaDate is double && (double)oaDate < DataUtils.MaxOADateValue)
1302 {
1303 DateTime date = DataUtils.GetDateFromOA((double)oaDate);
1304 if (date >= DataUtils.FirstAllowedExcelDate && date <= DataUtils.LastAllowedExcelDate)
1305 {
1306 return date;
1307 }
1308 }
1309 return data;
1310 }
1311
1318 private object ConvertTimeFromDouble(object data, ReaderOptions readerOptions)
1319 {
1320 object oaDate = ConvertToDouble(data, readerOptions);
1321 if (oaDate is double)
1322 {
1323 double d = (double)oaDate;
1324 if (d >= DataUtils.MinOADateValue && d <= DataUtils.MaxOADateValue)
1325 {
1326 DateTime date = DataUtils.GetDateFromOA(d);
1327 return new TimeSpan((int)d, date.Hour, date.Minute, date.Second);
1328 }
1329 }
1330 return data;
1331 }
1332
1338 private static int? TryConvertDoubleToInt(object data)
1339 {
1340 IConvertible converter = data as IConvertible;
1341 double dValue = converter.ToDouble(ReaderOptions.DefaultCultureInfo);
1342 if (dValue > int.MinValue && dValue < int.MaxValue)
1343 {
1344 return converter.ToInt32(ReaderOptions.DefaultCultureInfo);
1345 }
1346 return null;
1347 }
1348
1354 private static int ConvertDoubleToInt(object data)
1355 {
1356 IConvertible converter = data as IConvertible;
1357 return converter.ToInt32(ReaderOptions.DefaultCultureInfo);
1358 }
1359
1366 private string ConvertToString(object data, ReaderOptions readerOptions)
1367 {
1368 switch (data)
1369 {
1370 case int _:
1371 return ((int)data).ToString(ReaderOptions.DefaultCultureInfo);
1372 case uint _:
1373 return ((uint)data).ToString(ReaderOptions.DefaultCultureInfo);
1374 case long _:
1375 return ((long)data).ToString(ReaderOptions.DefaultCultureInfo);
1376 case ulong _:
1377 return ((ulong)data).ToString(ReaderOptions.DefaultCultureInfo);
1378 case float _:
1379 return ((float)data).ToString(ReaderOptions.DefaultCultureInfo);
1380 case double _:
1381 return ((double)data).ToString(ReaderOptions.DefaultCultureInfo);
1382 case bool _:
1383 return ((bool)data).ToString(ReaderOptions.DefaultCultureInfo);
1384 case DateTime _:
1385 return ((DateTime)data).ToString(readerOptions.DateTimeFormat, ParserUtils.InvariantCulture);
1386 case TimeSpan _:
1387 return ((TimeSpan)data).ToString(readerOptions.TimeSpanFormat, ParserUtils.InvariantCulture);
1388 default:
1389 if (data == null)
1390 {
1391 return null;
1392 }
1393 return data.ToString();
1394 }
1395 }
1396
1404 private object GetNumericValue(object raw, Cell.CellType importedType, ReaderOptions readerOptions)
1405 {
1406 if (raw == null)
1407 {
1408 return null;
1409 }
1410 object tempObject;
1411 switch (importedType)
1412 {
1413 case Cell.CellType.String:
1414 string tempString = raw.ToString();
1415 tempObject = GetNumericValue(tempString);
1416 if (tempObject != null)
1417 {
1418 return tempObject;
1419 }
1420 DateTime? tempDate = TryParseDate(tempString, readerOptions);
1421 if (tempDate != null)
1422 {
1423 return DataUtils.GetOADateTime(tempDate.Value);
1424 }
1425 TimeSpan? tempTime = TryParseTime(tempString, readerOptions);
1426 if (tempTime != null)
1427 {
1428 return DataUtils.GetOATime(tempTime.Value);
1429 }
1430 tempObject = ConvertToBool(raw, readerOptions);
1431 if (tempObject is bool)
1432 {
1433 return (bool)tempObject ? 1 : 0;
1434 }
1435 break;
1436 case Cell.CellType.Number:
1437 return raw;
1438 case Cell.CellType.Date:
1439 return DataUtils.GetOADateTime((DateTime)raw);
1440 case Cell.CellType.Time:
1441 return DataUtils.GetOATime((TimeSpan)raw);
1442 case Cell.CellType.Bool:
1443 if ((bool)raw)
1444 {
1445 return 1;
1446 }
1447 return 0;
1448 }
1449 return raw;
1450 }
1451
1452
1458 private static object GetNumericValue(string raw)
1459 {
1460 bool hasDecimalPoint = raw.Contains(".");
1461
1462 // Only try integer parsing if there's no decimal point
1463 if (!hasDecimalPoint)
1464 {
1465 // integer section (unchanged)
1466 uint uiValue;
1467 int iValue;
1468 bool canBeUint = ParserUtils.TryParseUint(raw, out uiValue);
1469 bool canBeInt = ParserUtils.TryParseInt(raw, out iValue);
1470 if (canBeUint && !canBeInt)
1471 {
1472 return uiValue;
1473 }
1474 else if (canBeInt)
1475 {
1476 return iValue;
1477 }
1478 ulong ulValue;
1479 long lValue;
1480 bool canBeUlong = ParserUtils.TryParseUlong(raw, out ulValue);
1481 bool canBeLong = ParserUtils.TryParseLong(raw, out lValue);
1482 if (canBeUlong && !canBeLong)
1483 {
1484 return ulValue;
1485 }
1486 else if (canBeLong)
1487 {
1488 return lValue;
1489 }
1490 }
1491
1492 decimal dcValue;
1493 double dValue;
1494 float fValue;
1495
1496 // Decimal/float section
1497 if (ParserUtils.TryParseDecimal(raw, out dcValue))
1498 {
1499 // Check if the value can be accurately represented as float
1500 float testFloat = decimal.ToSingle(dcValue);
1501 decimal backToDecimal = (decimal)testFloat;
1502
1503 // If converting to float and back preserves the value, use float
1504 if (dcValue == backToDecimal)
1505 {
1506 return testFloat;
1507 }
1508 else
1509 {
1510 // Otherwise use double for better precision
1511 return decimal.ToDouble(dcValue);
1512 }
1513 }
1514 // High range float section
1515 else if (ParserUtils.TryParseFloat(raw, out fValue) && fValue >= float.MinValue && fValue <= float.MaxValue && !float.IsInfinity(fValue))
1516 {
1517 return fValue;
1518 }
1519 if (ParserUtils.TryParseDouble(raw, out dValue))
1520 {
1521 return dValue;
1522 }
1523 return null;
1524 }
1525
1533 private float GetValidatedWidth(float rawValue, ReaderOptions readerOptions)
1534 {
1535 if (rawValue < Worksheet.MinColumnWidth)
1536 {
1537 if (readerOptions.EnforceStrictValidation)
1538 {
1539 throw new WorksheetException($"The worksheet contains an invalid column width (too small: {rawValue}) value. This error is ignored when disabling the reader option 'EnforceStrictValidation'");
1540 }
1541 else
1542 {
1543 return Worksheet.MinColumnWidth;
1544 }
1545 }
1546 else if (rawValue > Worksheet.MaxColumnWidth)
1547 {
1548 if (readerOptions.EnforceStrictValidation)
1549 {
1550 throw new WorksheetException($"The worksheet contains an invalid column width (too large: {rawValue}) value. This error is ignored when disabling the reader option 'EnforceStrictValidation'");
1551 }
1552 else
1553 {
1554 return Worksheet.MaxColumnWidth;
1555 }
1556 }
1557 else
1558 {
1559 return rawValue;
1560 }
1561 }
1562
1570 private float GetValidatedHeight(float rawValue, ReaderOptions readerOptions)
1571 {
1572 if (rawValue < Worksheet.MinRowHeight)
1573 {
1574 if (readerOptions.EnforceStrictValidation)
1575 {
1576 throw new WorksheetException($"The worksheet contains an invalid row height (too small: {rawValue}) value. Consider using the ImportOption 'EnforceValidRowDimensions' to ignore this error.");
1577 }
1578 else
1579 {
1580 return Worksheet.MinRowHeight;
1581 }
1582 }
1583 else if (rawValue > Worksheet.MaxRowHeight)
1584 {
1585 if (readerOptions.EnforceStrictValidation)
1586 {
1587 throw new WorksheetException($"The worksheet contains an invalid row height (too large: {rawValue}) value. Consider using the ImportOption 'EnforceValidRowDimensions' to ignore this error.");
1588 }
1589 else
1590 {
1591 return Worksheet.MaxRowHeight;
1592 }
1593 }
1594 else
1595 {
1596 return rawValue;
1597 }
1598 }
1599
1605 private string ResolveSharedString(string raw)
1606 {
1607 int stringId;
1608 if (ParserUtils.TryParseInt(raw, out stringId))
1609 {
1610 string resolvedString = SharedStrings.ElementAtOrDefault(stringId);
1611 if (resolvedString == null)
1612 {
1613 return raw;
1614 }
1615 else
1616 {
1617 return resolvedString;
1618 }
1619 }
1620 return raw;
1621 }
1622
1631 private Cell CreateCell(object value, Cell.CellType type, Address address, string styleNumber = null)
1632 {
1633 Cell cell = new Cell(value, type, address);
1634 if (styleNumber != null && resolvedStyles.TryGetValue(styleNumber, out var styleValue))
1635 {
1636 cell.SetStyle(styleValue);
1637 }
1638 return cell;
1639 }
1640 #endregion
1641 }
1642}
Class representing a reader for legacy passwords.
void Execute()
Method to execute the main logic of the plug-in (interface implementation).
List< String > SharedStrings
Gets or Sets the list of the shared strings. The index of the list corresponds to the index,...
int CurrentWorksheetID
Gets or sets the (r)ID of the current worksheet.
WorksheetReader()
Default constructor - Must be defined for instantiation of the plug-ins.
Workbook Workbook
Workbook reference where read data is stored (should not be null).
Action< MemoryStream, Workbook, string, IOptions, int?> InlinePluginHandler
Reference to the ReaderPlugInHandler, to be used for post operations in the Execute method.
void Init(MemoryStream stream, Workbook workbook, IOptions readerOptions, Action< MemoryStream, Workbook, string, IOptions, int?> inlinePluginHandler)
Initialization method (interface implementation).
Class representing a collection of pre-processed styles and their components. This class is internall...
Exceptions.IOException IOException