NanoXLSX.Reader 3.2.1
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.Text;
14using System.Xml;
15using NanoXLSX.Enums;
16using NanoXLSX.Exceptions;
17using NanoXLSX.Interfaces;
18using NanoXLSX.Interfaces.Reader;
19using NanoXLSX.Registry;
20using NanoXLSX.Styles;
21using NanoXLSX.Utils;
22using NanoXLSX.Utils.Xml;
23using static NanoXLSX.Enums.Password;
24using IOException = NanoXLSX.Exceptions.IOException;
25
27{
31 public class WorksheetReader : IWorksheetReader
32 {
33 #region privateFields
34 private Stream stream;
35 private HashSet<string> dateStyles;
36 private HashSet<string> timeStyles;
37 private Dictionary<string, Style> resolvedStyles;
38 private IPasswordReader passwordReader;
39 private ReaderOptions readerOptions;
40 #endregion
41
42 #region properties
43
47 public virtual string DocumentType { get { return @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"; } }
48
52 public Workbook Workbook { get; set; }
56 public IOptions Options { get; set; }
60 public Action<Stream, Workbook, string, IOptions, int?> InlinePluginHandler { get; set; }
64 public int CurrentWorksheetID { get; set; }
65
70 public List<String> SharedStrings { get; set; }
71 #endregion
72
73 #region constructors
78 {
79 }
80 #endregion
81
82 #region functions
90 public void Init(Stream stream, Workbook workbook, IOptions readerOptions, Action<Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
91 {
92 this.stream = stream;
93 this.Workbook = workbook;
94 this.Options = readerOptions;
95 this.readerOptions = readerOptions as ReaderOptions;
96 this.InlinePluginHandler = inlinePluginHandler;
97 if (dateStyles == null || timeStyles == null || this.resolvedStyles == null)
98 {
99 StyleReaderContainer styleReaderContainer = workbook.AuxiliaryData.GetData<StyleReaderContainer>(PlugInUUID.StyleReader, PlugInUUID.StyleEntity);
100 ProcessStyles(styleReaderContainer);
101 }
102 if (this.passwordReader == null)
103 {
104 this.passwordReader = PlugInLoader.GetPlugIn<IPasswordReader>(PlugInUUID.PasswordReader, new LegacyPasswordReader());
105 this.passwordReader.Init(PasswordType.WorksheetProtection, (ReaderOptions)readerOptions);
106 }
107 }
108
113 public void Execute()
114 {
115 try
116 {
117 WorksheetDefinition worksheetDefinition = Workbook.AuxiliaryData.GetData<WorksheetDefinition>(PlugInUUID.WorkbookReader, PlugInUUID.WorksheetDefinitionEntity, CurrentWorksheetID);
118 Worksheet worksheet = new Worksheet(worksheetDefinition.WorksheetName, worksheetDefinition.SheetID, Workbook)
119 {
120 Hidden = worksheetDefinition.Hidden
121 };
122 using (stream) // Close after processing
123 {
124 StringBuilder sb = new StringBuilder();
125 using (XmlReader reader = XmlReader.Create(stream, XmlStreamUtils.CreateSettings()))
126 {
127 while (reader.Read())
128 {
129 if (reader.NodeType != XmlNodeType.Element)
130 {
131 continue;
132 }
133 switch (reader.LocalName.ToLowerInvariant())
134 {
135 case "sheetviews":
136 GetSheetView(reader, worksheet);
137 break;
138 case "sheetformatpr":
139 GetSheetFormats(reader, worksheet);
140 break;
141 case "cols":
142 GetColumns(reader, worksheet, readerOptions);
143 break;
144 case "sheetdata":
145 GetRows(reader, worksheet, readerOptions, sb);
146 break;
147 case "sheetprotection":
148 GetSheetProtection(reader, worksheet);
149 break;
150 case "mergecells":
151 GetMergedCells(reader, worksheet);
152 break;
153 case "autofilter":
154 GetAutoFilters(reader, worksheet);
155 break;
156 }
157 }
158 SetWorkbookRelation(worksheet);
159 InlinePluginHandler?.Invoke(stream, Workbook, PlugInUUID.WorksheetInlineReader, Options, CurrentWorksheetID);
160 }
161 }
162 }
163 catch (NotSupportedContentException)
164 {
165 throw; // rethrow
166 }
167 catch (IOException)
168 {
169 throw;
170 }
171 catch (Exception ex)
172 {
173 throw new IOException("The XML entry could not be read from the input stream. Please see the inner exception:", ex);
174 }
175 }
176
181 private void SetWorkbookRelation(Worksheet worksheet)
182 {
183 Workbook.AddWorksheet(worksheet);
184 int selectedWorksheetId = Workbook.AuxiliaryData.GetData<int>(PlugInUUID.WorkbookReader, PlugInUUID.SelectedWorksheetEntity);
185 if (selectedWorksheetId == CurrentWorksheetID)
186 {
187 Workbook.SetSelectedWorksheet(worksheet);
188 }
189 }
190
195 private void ProcessStyles(StyleReaderContainer styleReaderContainer)
196 {
197 this.dateStyles = new HashSet<string>();
198 this.timeStyles = new HashSet<string>();
199 this.resolvedStyles = new Dictionary<string, Style>();
200 for (int i = 0; i < styleReaderContainer.StyleCount; i++)
201 {
202 bool isDate;
203 bool isTime;
204 string index = ParserUtils.ToString(i);
205 Style style = styleReaderContainer.GetStyle(i, out isDate, out isTime);
206 if (isDate)
207 {
208 this.dateStyles.Add(index);
209 }
210 if (isTime)
211 {
212 this.timeStyles.Add(index);
213 }
214 this.resolvedStyles.Add(index, style);
215 }
216 }
217
225 private void GetRows(XmlReader reader, Worksheet worksheet, ReaderOptions readerOptions, StringBuilder sb)
226 {
227 using (XmlReader sheetDataReader = reader.ReadSubtree())
228 {
229 sheetDataReader.Read(); // consume the sheetData open tag
230 while (sheetDataReader.Read())
231 {
232 if (!XmlStreamUtils.IsElement(sheetDataReader, "row"))
233 {
234 continue;
235 }
236 string rowAttribute = sheetDataReader.GetAttribute("r");
237 if (rowAttribute != null)
238 {
239 int rowNumber = ParserUtils.ParseInt(rowAttribute) - 1; // Transform to zero-based
240 string hiddenAttribute = sheetDataReader.GetAttribute("hidden");
241 if (hiddenAttribute != null && ParserUtils.ParseBinaryBool(hiddenAttribute) == 1)
242 {
243 worksheet.AddHiddenRow(rowNumber);
244 }
245 string heightAttribute = sheetDataReader.GetAttribute("ht");
246 if (heightAttribute != null)
247 {
248 worksheet.RowHeights.Add(rowNumber, GetValidatedHeight(ParserUtils.ParseFloat(heightAttribute), readerOptions));
249 }
250 }
251 if (!sheetDataReader.IsEmptyElement)
252 {
253 using (XmlReader rowReader = sheetDataReader.ReadSubtree())
254 {
255 rowReader.Read(); // consume the row open tag
256 while (rowReader.Read())
257 {
258 if (!XmlStreamUtils.IsElement(rowReader, "c"))
259 {
260 continue;
261 }
262 ReadCell(rowReader, worksheet, sb);
263 }
264 }
265 }
266 }
267 }
268 }
269
275 private static void GetSheetView(XmlReader reader, Worksheet worksheet)
276 {
277 using (XmlReader subtree = reader.ReadSubtree())
278 {
279 subtree.Read(); // consume sheetViews
280 while (subtree.Read())
281 {
282 if (!XmlStreamUtils.IsElement(subtree, "sheetView"))
283 {
284 continue;
285 }
286 string attribute = subtree.GetAttribute("view") ?? string.Empty;
287 worksheet.ViewType = Worksheet.GetSheetViewTypeEnum(attribute);
288 attribute = subtree.GetAttribute("zoomScale");
289 if (attribute != null)
290 {
291 worksheet.ZoomFactor = ParserUtils.ParseInt(attribute);
292 }
293 attribute = subtree.GetAttribute("zoomScaleNormal");
294 if (attribute != null)
295 {
296 worksheet.ZoomFactors[Worksheet.SheetViewType.Normal] = ParserUtils.ParseInt(attribute);
297 }
298 attribute = subtree.GetAttribute("zoomScalePageLayoutView");
299 if (attribute != null)
300 {
301 worksheet.ZoomFactors[Worksheet.SheetViewType.PageLayout] = ParserUtils.ParseInt(attribute);
302 }
303 attribute = subtree.GetAttribute("zoomScaleSheetLayoutView");
304 if (attribute != null)
305 {
306 worksheet.ZoomFactors[Worksheet.SheetViewType.PageBreakPreview] = ParserUtils.ParseInt(attribute);
307 }
308 attribute = subtree.GetAttribute("showGridLines");
309 if (attribute != null)
310 {
311 worksheet.ShowGridLines = ParserUtils.ParseBinaryBool(attribute) == 1;
312 }
313 attribute = subtree.GetAttribute("showRowColHeaders");
314 if (attribute != null)
315 {
316 worksheet.ShowRowColumnHeaders = ParserUtils.ParseBinaryBool(attribute) == 1;
317 }
318 attribute = subtree.GetAttribute("showRuler");
319 if (attribute != null)
320 {
321 worksheet.ShowRuler = ParserUtils.ParseBinaryBool(attribute) == 1;
322 }
323 using (XmlReader sheetViewReader = subtree.ReadSubtree())
324 {
325 sheetViewReader.Read(); // consume sheetView
326 while (sheetViewReader.Read())
327 {
328 if (sheetViewReader.NodeType != XmlNodeType.Element)
329 {
330 continue;
331 }
332 if (XmlStreamUtils.IsElement(sheetViewReader, "selection"))
333 {
334 attribute = sheetViewReader.GetAttribute("sqref");
335 if (attribute != null)
336 {
337 if (attribute.Contains(" "))
338 {
339 string[] ranges = attribute.Split(' ');
340 foreach (string range in ranges)
341 {
342 CollectSelectedCells(range, worksheet);
343 }
344 }
345 else
346 {
347 CollectSelectedCells(attribute, worksheet);
348 }
349 }
350 }
351 else if (XmlStreamUtils.IsElement(sheetViewReader, "pane"))
352 {
353 SetPaneSplit(sheetViewReader, worksheet);
354 }
355 }
356 }
357 }
358 }
359 }
360
366 private static void CollectSelectedCells(string attribute, Worksheet worksheet)
367 {
368 if (attribute.Contains(":"))
369 {
370 // One range
371 worksheet.AddSelectedCells(new Range(attribute));
372 }
373 else
374 {
375 // One cell
376 worksheet.AddSelectedCells(new Range(attribute + ":" + attribute));
377 }
378 }
379
385 private static void SetPaneSplit(XmlReader reader, Worksheet worksheet)
386 {
387 string attribute = reader.GetAttribute("state");
388 bool useNumbers = false;
389 bool frozenState = false;
390 bool ySplitDefined = false;
391 bool xSplitDefined = false;
392 int? paneSplitRowIndex = null;
393 int? paneSplitColumnIndex = null;
394 float? paneSplitHeight = null;
395 float? paneSplitWidth = null;
396 Address topLeftCell = new Address(0, 0); // default value
397 Worksheet.WorksheetPane? activePane = null;
398 if (attribute != null)
399 {
400 if (ParserUtils.ToLower(attribute) == "frozen" || ParserUtils.ToLower(attribute) == "frozensplit")
401 {
402 frozenState = true;
403 }
404 useNumbers = frozenState;
405 }
406 attribute = reader.GetAttribute("ySplit");
407 if (attribute != null)
408 {
409 ySplitDefined = true;
410 if (useNumbers)
411 {
412 paneSplitRowIndex = ParserUtils.ParseInt(attribute);
413 }
414 else
415 {
416 paneSplitHeight = DataUtils.GetPaneSplitHeight(ParserUtils.ParseFloat(attribute));
417 }
418 }
419 attribute = reader.GetAttribute("xSplit");
420 if (attribute != null)
421 {
422 xSplitDefined = true;
423 if (useNumbers)
424 {
425 paneSplitColumnIndex = ParserUtils.ParseInt(attribute);
426 }
427 else
428 {
429 paneSplitWidth = DataUtils.GetPaneSplitWidth(ParserUtils.ParseFloat(attribute));
430 }
431 }
432 attribute = reader.GetAttribute("topLeftCell");
433 if (attribute != null)
434 {
435 topLeftCell = new Address(attribute);
436 }
437 attribute = reader.GetAttribute("activePane") ?? string.Empty;
438 activePane = Worksheet.GetWorksheetPaneEnum(attribute);
439 if (frozenState)
440 {
441 if (ySplitDefined && !xSplitDefined)
442 {
443 worksheet.SetHorizontalSplit(paneSplitRowIndex.Value, frozenState, topLeftCell, activePane);
444 }
445 if (!ySplitDefined && xSplitDefined)
446 {
447 worksheet.SetVerticalSplit(paneSplitColumnIndex.Value, frozenState, topLeftCell, activePane);
448 }
449 else if (ySplitDefined && xSplitDefined)
450 {
451 worksheet.SetSplit(paneSplitColumnIndex.Value, paneSplitRowIndex.Value, frozenState, topLeftCell, activePane);
452 }
453 }
454 else
455 {
456 if (ySplitDefined && !xSplitDefined)
457 {
458 worksheet.SetHorizontalSplit(paneSplitHeight.Value, topLeftCell, activePane);
459 }
460 if (!ySplitDefined && xSplitDefined)
461 {
462 worksheet.SetVerticalSplit(paneSplitWidth.Value, topLeftCell, activePane);
463 }
464 else if (ySplitDefined && xSplitDefined)
465 {
466 worksheet.SetSplit(paneSplitWidth, paneSplitHeight, topLeftCell, activePane);
467 }
468 }
469 }
470
479 private void GetSheetProtection(XmlReader reader, Worksheet worksheet)
480 {
481 int hasProtection = 0;
482 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.AutoFilter, worksheet);
483 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.DeleteColumns, worksheet);
484 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.DeleteRows, worksheet);
485 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.FormatCells, worksheet);
486 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.FormatColumns, worksheet);
487 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.FormatRows, worksheet);
488 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.InsertColumns, worksheet);
489 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.InsertHyperlinks, worksheet);
490 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.InsertRows, worksheet);
491 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.Objects, worksheet);
492 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.PivotTables, worksheet);
493 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.Scenarios, worksheet);
494 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.SelectLockedCells, worksheet);
495 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.SelectUnlockedCells, worksheet);
496 hasProtection += ReadSheetProtectionAttribute(reader, Worksheet.SheetProtectionValue.Sort, worksheet);
497 if (hasProtection > 0)
498 {
499 worksheet.UseSheetProtection = true;
500 }
501 string outerXml;
502 using (XmlReader subtree = reader.ReadSubtree())
503 {
504 subtree.MoveToContent();
505 outerXml = subtree.ReadOuterXml();
506 }
507 XmlDocument miniDoc = new XmlDocument { XmlResolver = null };
508 miniDoc.LoadXml(outerXml);
509 this.passwordReader.ReadXmlAttributes(miniDoc.DocumentElement);
510 if (this.passwordReader.PasswordIsSet())
511 {
512 if (this.passwordReader is LegacyPasswordReader && (this.passwordReader as LegacyPasswordReader).ContemporaryAlgorithmDetected && (readerOptions == null || !readerOptions.IgnoreNotSupportedPasswordAlgorithms))
513 {
514 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");
515 }
516 worksheet.SheetProtectionPassword.CopyFrom(this.passwordReader);
517 }
518 }
519
527 private static int ReadSheetProtectionAttribute(XmlReader reader, Worksheet.SheetProtectionValue sheetProtectionValue, Worksheet worksheet)
528 {
529 string attrName = Worksheet.GetSheetProtectionName(sheetProtectionValue);
530 if (reader.GetAttribute(attrName) != null)
531 {
532 worksheet.SheetProtectionValues.Add(sheetProtectionValue);
533 return 1;
534 }
535 return 0;
536 }
537
543 private static void GetMergedCells(XmlReader reader, Worksheet worksheet)
544 {
545 using (XmlReader subtree = reader.ReadSubtree())
546 {
547 subtree.Read(); // consume the mergeCells open tag
548 while (subtree.Read())
549 {
550 if (!XmlStreamUtils.IsElement(subtree, "mergeCell"))
551 {
552 continue;
553 }
554 string attribute = subtree.GetAttribute("ref");
555 if (attribute != null)
556 {
557 worksheet.MergeCells(new Range(attribute));
558 }
559 }
560 }
561 }
562
568 private static void GetSheetFormats(XmlReader reader, Worksheet worksheet)
569 {
570 string attribute = reader.GetAttribute("defaultColWidth");
571 if (attribute != null)
572 {
573 worksheet.DefaultColumnWidth = ParserUtils.ParseFloat(attribute);
574 }
575 attribute = reader.GetAttribute("defaultRowHeight");
576 if (attribute != null)
577 {
578 worksheet.DefaultRowHeight = ParserUtils.ParseFloat(attribute);
579 }
580 }
581
587 private static void GetAutoFilters(XmlReader reader, Worksheet worksheet)
588 {
589 string autoFilterRef = reader.GetAttribute("ref");
590 if (autoFilterRef != null)
591 {
592 Range range = new Range(autoFilterRef);
593 worksheet.SetAutoFilter(range.StartAddress.Column, range.EndAddress.Column);
594 }
595 }
596
603 private void GetColumns(XmlReader reader, Worksheet worksheet, ReaderOptions readerOptions)
604 {
605 using (XmlReader subtree = reader.ReadSubtree())
606 {
607 subtree.Read(); // consume the cols open tag
608 while (subtree.Read())
609 {
610 if (!XmlStreamUtils.IsElement(subtree, "col"))
611 {
612 continue;
613 }
614 int? min = null;
615 int? max = null;
616 List<int> indices = new List<int>();
617 string attribute = subtree.GetAttribute("min");
618 if (attribute != null)
619 {
620 min = ParserUtils.ParseInt(attribute);
621 max = min;
622 indices.Add(min.Value);
623 }
624 attribute = subtree.GetAttribute("max");
625 if (attribute != null)
626 {
627 max = ParserUtils.ParseInt(attribute);
628 }
629 if (min != null && max.Value != min.Value)
630 {
631 for (int i = min.Value; i <= max.Value; i++)
632 {
633 indices.Add(i);
634 }
635 }
636 attribute = subtree.GetAttribute("width");
637 float width = Worksheet.DefaultWorksheetColumnWidth;
638 if (attribute != null)
639 {
640 width = ParserUtils.ParseFloat(attribute);
641 }
642 attribute = subtree.GetAttribute("hidden");
643 bool hidden = false;
644 if (attribute != null && ParserUtils.ParseBinaryBool(attribute) == 1)
645 {
646 hidden = true;
647 }
648 attribute = subtree.GetAttribute("style");
649 Style defaultStyle = null;
650 if (attribute != null && resolvedStyles.TryGetValue(attribute, out var attributeValue))
651 {
652 defaultStyle = attributeValue;
653 }
654 foreach (int index in indices)
655 {
656 string columnAddress = Cell.ResolveColumnAddress(index - 1); // Transform to zero-based
657 if (defaultStyle != null)
658 {
659 worksheet.SetColumnDefaultStyle(columnAddress, defaultStyle);
660 }
661 if (width != Worksheet.DefaultWorksheetColumnWidth)
662 {
663 worksheet.SetColumnWidth(columnAddress, GetValidatedWidth(width, readerOptions));
664 }
665 if (hidden)
666 {
667 worksheet.AddHiddenColumn(columnAddress);
668 }
669 }
670 }
671 }
672 }
673
680 private void ReadCell(XmlReader rowReader, Worksheet worksheet, StringBuilder sb)
681 {
682 string address = rowReader.GetAttribute("r"); // Mandatory
683 string type = rowReader.GetAttribute("t"); // can be null
684 string styleNumber = rowReader.GetAttribute("s"); // can be null
685 string value = "";
686 string cachedValue = null;
687 bool hasCachedValue = false;
688 bool hasInlineString = false;
689 string formulaExpression = null;
690 bool hasFormula = false;
691 string formulaType = null;
692 string formulaReference = null;
693 if (!rowReader.IsEmptyElement)
694 {
695 using (XmlReader cellReader = rowReader.ReadSubtree())
696 {
697 cellReader.Read(); // consume <c>
698 while (cellReader.Read())
699 {
700 if (cellReader.NodeType != XmlNodeType.Element)
701 {
702 continue;
703 }
704 if (cellReader.LocalName.Equals("f", StringComparison.OrdinalIgnoreCase))
705 {
706 formulaType = cellReader.GetAttribute("t"); // Can be null
707 formulaReference = cellReader.GetAttribute("ref"); // Can be null
708 formulaExpression = cellReader.ReadElementContentAsString();
709 hasFormula = true;
710 }
711 if (cellReader.LocalName.Equals("v", StringComparison.OrdinalIgnoreCase))
712 {
713 if (hasFormula)
714 {
715 cachedValue = cellReader.ReadElementContentAsString();
716 hasCachedValue = true;
717 }
718 else
719 {
720 value = cellReader.ReadElementContentAsString();
721 }
722 }
723 else if (cellReader.LocalName.Equals("is", StringComparison.OrdinalIgnoreCase))
724 {
725 sb.Clear();
726 using (XmlReader isReader = cellReader.ReadSubtree())
727 {
728 isReader.Read(); // consume <is>
729 while (isReader.Read())
730 {
731 if (isReader.NodeType == XmlNodeType.Element &&
732 isReader.LocalName.Equals("t", StringComparison.OrdinalIgnoreCase))
733 {
734 sb.Append(isReader.ReadElementContentAsString());
735 }
736 }
737 }
738 value = sb.ToString();
739 hasInlineString = true;
740 }
741 }
742 }
743 }
744 if (hasFormula)
745 {
746 if (type == "s" && hasCachedValue)
747 {
748 cachedValue = ResolveSharedString(cachedValue)?.ToString();
749 }
750 else if (type == "inlineStr" && hasInlineString)
751 {
752 cachedValue = value;
753 hasCachedValue = true;
754 }
755 value = formulaExpression;
756 }
757 else if (type == "str" && !hasFormula)
758 {
759 // Linked cell of a formula. The master cell will be resolved later in a finalizing processor
760 cachedValue = value; // Value is actually cached value (value is kept for compatibility)
761 hasCachedValue = true;
762 hasFormula = true; // Triggers upsert
763 }
764 Cell cell = hasFormula
765 ? CreateCell(value, Cell.CellType.Formula, new Address(address), styleNumber)
766 : ResolveCellData(value, type, styleNumber, address);
767 UpsertFormulaData(cell, formulaExpression, cachedValue, hasCachedValue, type, formulaType, formulaReference);
768 worksheet.AddCell(cell, address);
769 }
770
779 private Cell ResolveCellData(string raw, string type, string styleNumber, string address)
780 {
781 Cell.CellType importedType = Cell.CellType.Default;
782 object rawValue;
783 if (type == "e")
784 {
785 importedType = Cell.CellType.Error;
786 if (!Errors.TryParseFormulaError(raw, out Errors.FormulaError error))
787 {
788 error = Errors.FormulaError.UnknownError;
789 }
790 rawValue = error;
791 }
792 else if (type == "b")
793 {
794 rawValue = TryParseBool(raw);
795 if (rawValue != null)
796 {
797 importedType = Cell.CellType.Bool;
798 }
799 else
800 {
801 rawValue = GetNumericValue(raw);
802 if (rawValue != null)
803 {
804 importedType = Cell.CellType.Number;
805 }
806 }
807 }
808 else if (type == "s")
809 {
810 importedType = Cell.CellType.String;
811 rawValue = ResolveSharedString(raw);
812 }
813 else if (type == "inlineStr")
814 {
815 importedType = Cell.CellType.String;
816 rawValue = raw;
817 }
818 else if (dateStyles.Contains(styleNumber) && (type == null || type == "" || type == "n"))
819 {
820 rawValue = GetDateTimeValue(raw, Cell.CellType.Date, out importedType);
821 }
822 else if (timeStyles.Contains(styleNumber) && (type == null || type == "" || type == "n"))
823 {
824 rawValue = GetDateTimeValue(raw, Cell.CellType.Time, out importedType);
825 }
826 else
827 {
828 importedType = Cell.CellType.Number;
829 rawValue = GetNumericValue(raw);
830 }
831 if (rawValue == null && raw == "")
832 {
833 importedType = Cell.CellType.Empty;
834 rawValue = null;
835 }
836 else if (rawValue == null && raw.Length > 0)
837 {
838 importedType = Cell.CellType.String;
839 rawValue = raw;
840 }
841 Address cellAddress = new Address(address);
842 if (readerOptions != null)
843 {
844 if (readerOptions.EnforcedColumnTypes.Count > 0)
845 {
846 rawValue = GetEnforcedColumnValue(rawValue, importedType, cellAddress);
847 }
848 rawValue = GetGloballyEnforcedValue(rawValue, cellAddress);
849 rawValue = GetGloballyEnforcedFlagValues(rawValue, cellAddress);
850 importedType = ResolveType(rawValue);
851 if (importedType == Cell.CellType.Date && rawValue is DateTime && (DateTime)rawValue < DataUtils.FirstAllowedExcelDate)
852 {
853 // Fix conversion from time to date, where time has no days
854 rawValue = ((DateTime)rawValue).AddDays(1);
855 }
856 }
857 return CreateCell(rawValue, importedType, cellAddress, styleNumber);
858 }
859
870 private static void UpsertFormulaData(Cell cell, string expression, string cachedValue, bool hasCachedValue, string cachedValueType, string formulaType, string formulaReference)
871 {
872 if (cell.DataType != Cell.CellType.Formula)
873 {
874 return;
875 }
876 FormulaData formula = cell.Formula;
877 Cell.CellType resolvedCachedValueType = ResolveFormulaCachedValueType(cachedValueType, hasCachedValue);
878 formula.Expression = expression;
879 if (resolvedCachedValueType == Cell.CellType.Error)
880 {
881 if (!Errors.TryParseFormulaError(cachedValue, out Errors.FormulaError error))
882 {
883 error = Errors.FormulaError.UnknownError;
884 }
885 formula.CachedValue = error;
886 }
887 else
888 {
889 formula.CachedValue = cachedValue;
890 }
891 formula.CachedValueType = resolvedCachedValueType;
892 formula.FormulaRange = formulaReference;
893 if (!string.IsNullOrEmpty(formulaType))
894 {
895 switch (formulaType)
896 {
897 case "array":
898 formula.Type = FormulaData.FormulaType.Array;
899 break;
900 case "dataTable":
901 formula.Type = FormulaData.FormulaType.DataTable;
902 break;
903 case "shared":
904 formula.Type = FormulaData.FormulaType.Shared;
905 break;
906 default:
907 formula.Type = FormulaData.FormulaType.Normal;
908 break;
909 }
910 }
911 else
912 {
913 formula.Type = FormulaData.FormulaType.Normal; // Default
914 }
915 // Note: Defined name and formula master cell (array) resolution has to be performed in a finalizing processor
916 cell.Formula = formula;
917 }
918
925 private static Cell.CellType ResolveFormulaCachedValueType(string type, bool hasCachedValue)
926 {
927 if (!hasCachedValue)
928 {
929 return Cell.CellType.Default;
930 }
931 switch (type)
932 {
933 case null:
934 case "":
935 case "n":
936 return Cell.CellType.Number;
937 case "str":
938 case "s":
939 case "inlineStr":
940 return Cell.CellType.String;
941 case "b":
942 return Cell.CellType.Bool;
943 case "e":
944 return Cell.CellType.Error;
945 case "d":
946 return Cell.CellType.Date;
947 default:
948 return Cell.CellType.Default;
949 }
950 }
951
957 private static Cell.CellType ResolveType(object value)
958 {
959 if (value == null)
960 {
961 return Cell.CellType.Empty;
962 }
963 switch (value)
964 {
965 case uint _:
966 case long _:
967 case ulong _:
968 case short _:
969 case ushort _:
970 case decimal _:
971 case float _:
972 case double _:
973 case byte _:
974 case sbyte _:
975 case int _:
976 return Cell.CellType.Number;
977 case DateTime _:
978 return Cell.CellType.Date;
979 case TimeSpan _:
980 return Cell.CellType.Time;
981 case bool _:
982 return Cell.CellType.Bool;
983 case Errors.FormulaError _:
984 return Cell.CellType.Error;
985 default:
986 return Cell.CellType.String;
987 }
988 }
989
996 private object GetGloballyEnforcedFlagValues(object data, Address address)
997 {
998 if (address.Row < readerOptions.EnforcingStartRowNumber)
999 {
1000 return data;
1001 }
1002 if (readerOptions.EnforceDateTimesAsNumbers)
1003 {
1004 if (data is DateTime)
1005 {
1006 data = DataUtils.GetOADateTime((DateTime)data, true);
1007 }
1008 else if (data is TimeSpan)
1009 {
1010 data = DataUtils.GetOATime((TimeSpan)data);
1011 }
1012 }
1013 if (readerOptions.EnforceEmptyValuesAsString && data == null)
1014 {
1015 return "";
1016 }
1017 return data;
1018 }
1019
1026 private object GetGloballyEnforcedValue(object data, Address address)
1027 {
1028 if (address.Row < readerOptions.EnforcingStartRowNumber)
1029 {
1030 return data;
1031 }
1032 if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToDouble)
1033 {
1034 object tempDouble = ConvertToDouble(data, readerOptions);
1035 if (tempDouble != null)
1036 {
1037 return tempDouble;
1038 }
1039 }
1040 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToDecimal)
1041 {
1042 object tempDecimal = ConvertToDecimal(data, readerOptions);
1043 if (tempDecimal != null)
1044 {
1045 return tempDecimal;
1046 }
1047 }
1048 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToInt)
1049 {
1050 object tempInt = ConvertToInt(data);
1051 if (tempInt != null)
1052 {
1053 return tempInt;
1054 }
1055 }
1056 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.EverythingToString)
1057 {
1058 return ConvertToString(data, readerOptions);
1059 }
1060 return data;
1061 }
1062
1070 private object GetEnforcedColumnValue(object data, Cell.CellType importedTyp, Address address)
1071 {
1072 if (address.Row < readerOptions.EnforcingStartRowNumber)
1073 {
1074 return data;
1075 }
1076 if (!readerOptions.EnforcedColumnTypes.TryGetValue(address.Column, out var columnType))
1077 {
1078 return data;
1079 }
1080 switch (columnType)
1081 {
1082 case ReaderOptions.ColumnType.Numeric:
1083 return GetNumericValue(data, importedTyp, readerOptions);
1084 case ReaderOptions.ColumnType.Decimal:
1085 return ConvertToDecimal(data, readerOptions);
1086 case ReaderOptions.ColumnType.Double:
1087 return ConvertToDouble(data, readerOptions);
1088 case ReaderOptions.ColumnType.Date:
1089 return ConvertToDate(data, readerOptions);
1090 case ReaderOptions.ColumnType.Time:
1091 return ConvertToTime(data, readerOptions);
1092 case ReaderOptions.ColumnType.Bool:
1093 return ConvertToBool(data, readerOptions);
1094 default:
1095 return ConvertToString(data, readerOptions);
1096 }
1097 }
1098
1105 private object ConvertToBool(object data, ReaderOptions readerOptions)
1106 {
1107 switch (data)
1108 {
1109 case bool _:
1110 return data;
1111 case uint _:
1112 case long _:
1113 case ulong _:
1114 case short _:
1115 case ushort _:
1116 case float _:
1117 case byte _:
1118 case sbyte _:
1119 case int _:
1120 object tempObject = ConvertToDouble(data, readerOptions);
1121 if (tempObject is double)
1122 {
1123 double tempDouble = (double)tempObject;
1124 if (double.Equals(tempDouble, 0d))
1125 {
1126 return false;
1127 }
1128 else if (double.Equals(tempDouble, 1d))
1129 {
1130 return true;
1131 }
1132 }
1133 break;
1134 case string _:
1135
1136 string tempString = (string)data;
1137 bool? tempBool = TryParseBool(tempString);
1138 if (tempBool != null)
1139 {
1140 return tempBool.Value;
1141 }
1142 break;
1143 }
1144 return data;
1145 }
1146
1152 private static bool? TryParseBool(string raw)
1153 {
1154 if (raw == "0")
1155 {
1156 return false;
1157 }
1158 else if (raw == "1")
1159 {
1160 return true;
1161 }
1162 else
1163 {
1164 bool value;
1165 if (bool.TryParse(raw, out value))
1166 {
1167 return value;
1168 }
1169 else
1170 {
1171 return null;
1172 }
1173 }
1174 }
1175
1182 private object ConvertToDouble(object data, ReaderOptions readerOptions)
1183 {
1184 object value = ConvertToDecimal(data, readerOptions);
1185 if (value is decimal)
1186 {
1187 return Decimal.ToDouble((decimal)value);
1188 }
1189 else if (value is float)
1190 {
1191 return Convert.ToDouble((float)value);
1192 }
1193 return value;
1194 }
1195
1202 private object ConvertToDecimal(object data, ReaderOptions readerOptions)
1203 {
1204 IConvertible converter;
1205 switch (data)
1206 {
1207 case double doubleValue:
1208 try
1209 {
1210 return Convert.ToDecimal(doubleValue);
1211 }
1212 catch (OverflowException)
1213 {
1214 return data;
1215 }
1216 case uint _:
1217 case long _:
1218 case ulong _:
1219 case short _:
1220 case ushort _:
1221 case float _:
1222 case byte _:
1223 case sbyte _:
1224 case int _:
1225 converter = data as IConvertible;
1226 double tempDouble = converter.ToDouble(DataUtils.InvariantCulture);
1227 if (tempDouble > (double)decimal.MaxValue || tempDouble < (double)decimal.MinValue)
1228 {
1229 return data;
1230 }
1231 else
1232 {
1233 return converter.ToDecimal(DataUtils.InvariantCulture);
1234 }
1235 case bool _:
1236 if ((bool)data)
1237 {
1238 return decimal.One;
1239 }
1240 else
1241 {
1242 return decimal.Zero;
1243 }
1244 case DateTime _:
1245 return new decimal(DataUtils.GetOADateTime((DateTime)data));
1246 case TimeSpan _:
1247 return new decimal(DataUtils.GetOATime((TimeSpan)data));
1248 case string _:
1249 decimal dValue;
1250 string tempString = (string)data;
1251 if (ParserUtils.TryParseDecimal(tempString, out dValue))
1252 {
1253 return dValue;
1254 }
1255 DateTime? tempDate = TryParseDate(tempString, readerOptions);
1256 if (tempDate != null)
1257 {
1258 return new decimal(DataUtils.GetOADateTime(tempDate.Value));
1259 }
1260 TimeSpan? tempTime = TryParseTime(tempString, readerOptions);
1261 if (tempTime != null)
1262 {
1263 return new decimal(DataUtils.GetOATime(tempTime.Value));
1264 }
1265 break;
1266 }
1267 return data;
1268 }
1269
1275 private static object ConvertToInt(object data)
1276 {
1277 double tempDouble;
1278 switch (data)
1279 {
1280 case uint _:
1281 case long _:
1282 case ulong _:
1283 break;
1284 case DateTime _:
1285 tempDouble = DataUtils.GetOADateTime((DateTime)data, true);
1286 return ConvertDoubleToInt(tempDouble);
1287 case TimeSpan _:
1288 tempDouble = DataUtils.GetOATime((TimeSpan)data);
1289 return ConvertDoubleToInt(tempDouble);
1290 case float _:
1291 case double _:
1292 int? tempInt = TryConvertDoubleToInt(data);
1293 if (tempInt != null)
1294 {
1295 return tempInt;
1296 }
1297 break;
1298 case bool _:
1299 return (bool)data ? 1 : 0;
1300 case string _:
1301 int tempInt2;
1302 if (ParserUtils.TryParseInt((string)data, out tempInt2))
1303 {
1304 return tempInt2;
1305 }
1306 break;
1307 }
1308 return null;
1309 }
1310
1317 private object ConvertToDate(object data, ReaderOptions readerOptions)
1318 {
1319 switch (data)
1320 {
1321 case DateTime _:
1322 return data;
1323 case TimeSpan _:
1324 DateTime root = DataUtils.FirstAllowedExcelDate;
1325 TimeSpan time = (TimeSpan)data;
1326 root = root.AddDays(-1); // Fix offset of 1
1327 root = root.AddHours(time.Hours);
1328 root = root.AddMinutes(time.Minutes);
1329 root = root.AddSeconds(time.Seconds);
1330 return root;
1331 case double _:
1332 case uint _:
1333 case long _:
1334 case ulong _:
1335 case short _:
1336 case ushort _:
1337 case float _:
1338 case byte _:
1339 case sbyte _:
1340 case int _:
1341 return ConvertDateFromDouble(data, readerOptions);
1342 case string _:
1343 DateTime? date2 = TryParseDate((string)data, readerOptions);
1344 if (date2 != null)
1345 {
1346 return date2.Value;
1347 }
1348 return ConvertDateFromDouble(data, readerOptions);
1349 }
1350 return data;
1351 }
1352
1359 private DateTime? TryParseDate(string raw, ReaderOptions readerOptions)
1360 {
1361 DateTime dateTime;
1362 bool isDateTime;
1363 if (readerOptions == null || string.IsNullOrEmpty(readerOptions.DateTimeFormat) || readerOptions.TemporalCultureInfo == null)
1364 {
1365 isDateTime = DateTime.TryParse(raw, ReaderOptions.DefaultCultureInfo, DateTimeStyles.None, out dateTime);
1366 }
1367 else
1368 {
1369 isDateTime = DateTime.TryParseExact(raw, readerOptions.DateTimeFormat, readerOptions.TemporalCultureInfo, DateTimeStyles.None, out dateTime);
1370 }
1371 if (isDateTime && dateTime >= DataUtils.FirstAllowedExcelDate && dateTime <= DataUtils.LastAllowedExcelDate)
1372 {
1373 return dateTime;
1374 }
1375 return null;
1376 }
1377
1384 private object ConvertToTime(object data, ReaderOptions readerOptions)
1385 {
1386 switch (data)
1387 {
1388 case DateTime _:
1389 return ConvertTimeFromDouble(data, readerOptions);
1390 case TimeSpan _:
1391 return data;
1392 case double _:
1393 case uint _:
1394 case long _:
1395 case ulong _:
1396 case short _:
1397 case ushort _:
1398 case float _:
1399 case byte _:
1400 case sbyte _:
1401 case int _:
1402 return ConvertTimeFromDouble(data, readerOptions);
1403 case string _:
1404 TimeSpan? time = TryParseTime((string)data, readerOptions);
1405 if (time != null)
1406 {
1407 return time;
1408 }
1409 return ConvertTimeFromDouble(data, readerOptions);
1410 }
1411 return data;
1412 }
1413
1420 private static TimeSpan? TryParseTime(string raw, ReaderOptions readerOptions)
1421 {
1422 TimeSpan timeSpan;
1423 bool isTimeSpan;
1424 if (readerOptions == null || string.IsNullOrEmpty(readerOptions.TimeSpanFormat) || readerOptions.TemporalCultureInfo == null)
1425 {
1426 isTimeSpan = TimeSpan.TryParse(raw, ReaderOptions.DefaultCultureInfo, out timeSpan);
1427 }
1428 else
1429 {
1430 isTimeSpan = TimeSpan.TryParseExact(raw, readerOptions.TimeSpanFormat, readerOptions.TemporalCultureInfo, out timeSpan);
1431 }
1432 if (isTimeSpan && timeSpan.Days >= 0 && timeSpan.Days < DataUtils.MaxOADateValue)
1433 {
1434 return timeSpan;
1435 }
1436 return null;
1437 }
1438
1447 private static object GetDateTimeValue(string raw, Cell.CellType valueType, out Cell.CellType resolvedType)
1448 {
1449 double dValue;
1450 if (!ParserUtils.TryParseDouble(raw, out dValue))
1451 {
1452 resolvedType = Cell.CellType.String;
1453 return raw;
1454 }
1455 if ((valueType == Cell.CellType.Date && (dValue < DataUtils.MinOADateValue || dValue > DataUtils.MaxOADateValue)) || (valueType == Cell.CellType.Time && (dValue < 0.0 || dValue > DataUtils.MaxOADateValue)))
1456 {
1457 // fallback to number (cannot be anything else)
1458 resolvedType = Cell.CellType.Number;
1459 return GetNumericValue(raw);
1460 }
1461 DateTime tempDate = DataUtils.GetDateFromOA(dValue);
1462 if (dValue < 1.0)
1463 {
1464 tempDate = tempDate.AddDays(1); // Modify wrong 1st date when < 1
1465 }
1466 if (valueType == Cell.CellType.Date)
1467 {
1468 resolvedType = Cell.CellType.Date;
1469 return tempDate;
1470 }
1471 else
1472 {
1473 resolvedType = Cell.CellType.Time;
1474 return new TimeSpan((int)dValue, tempDate.Hour, tempDate.Minute, tempDate.Second);
1475 }
1476 }
1477
1484 private object ConvertDateFromDouble(object data, ReaderOptions readerOptions)
1485 {
1486 object oaDate = ConvertToDouble(data, readerOptions);
1487 if (oaDate is double && (double)oaDate < DataUtils.MaxOADateValue)
1488 {
1489 DateTime date = DataUtils.GetDateFromOA((double)oaDate);
1490 if (date >= DataUtils.FirstAllowedExcelDate && date <= DataUtils.LastAllowedExcelDate)
1491 {
1492 return date;
1493 }
1494 }
1495 return data;
1496 }
1497
1504 private object ConvertTimeFromDouble(object data, ReaderOptions readerOptions)
1505 {
1506 object oaDate = ConvertToDouble(data, readerOptions);
1507 if (oaDate is double)
1508 {
1509 double d = (double)oaDate;
1510 if (d >= DataUtils.MinOADateValue && d <= DataUtils.MaxOADateValue)
1511 {
1512 DateTime date = DataUtils.GetDateFromOA(d);
1513 return new TimeSpan((int)d, date.Hour, date.Minute, date.Second);
1514 }
1515 }
1516 return data;
1517 }
1518
1524 private static int? TryConvertDoubleToInt(object data)
1525 {
1526 IConvertible converter = data as IConvertible;
1527 double dValue = converter.ToDouble(ReaderOptions.DefaultCultureInfo);
1528 if (dValue > int.MinValue && dValue < int.MaxValue)
1529 {
1530 return converter.ToInt32(ReaderOptions.DefaultCultureInfo);
1531 }
1532 return null;
1533 }
1534
1540 private static int ConvertDoubleToInt(object data)
1541 {
1542 IConvertible converter = data as IConvertible;
1543 return converter.ToInt32(ReaderOptions.DefaultCultureInfo);
1544 }
1545
1552 private static string ConvertToString(object data, ReaderOptions readerOptions)
1553 {
1554 switch (data)
1555 {
1556 case int _:
1557 return ((int)data).ToString(ReaderOptions.DefaultCultureInfo);
1558 case uint _:
1559 return ((uint)data).ToString(ReaderOptions.DefaultCultureInfo);
1560 case long _:
1561 return ((long)data).ToString(ReaderOptions.DefaultCultureInfo);
1562 case ulong _:
1563 return ((ulong)data).ToString(ReaderOptions.DefaultCultureInfo);
1564 case float _:
1565 return ((float)data).ToString(ReaderOptions.DefaultCultureInfo);
1566 case double _:
1567 return ((double)data).ToString(ReaderOptions.DefaultCultureInfo);
1568 case bool _:
1569 return ((bool)data).ToString(ReaderOptions.DefaultCultureInfo);
1570 case DateTime _:
1571 return ((DateTime)data).ToString(readerOptions.DateTimeFormat, ParserUtils.InvariantCulture);
1572 case TimeSpan _:
1573 return ((TimeSpan)data).ToString(readerOptions.TimeSpanFormat, ParserUtils.InvariantCulture);
1574 default:
1575 if (data == null)
1576 {
1577 return null;
1578 }
1579 return data.ToString();
1580 }
1581 }
1582
1590 private object GetNumericValue(object raw, Cell.CellType importedType, ReaderOptions readerOptions)
1591 {
1592 if (raw == null)
1593 {
1594 return null;
1595 }
1596 object tempObject;
1597 switch (importedType)
1598 {
1599 case Cell.CellType.String:
1600 string tempString = raw.ToString();
1601 tempObject = GetNumericValue(tempString);
1602 if (tempObject != null)
1603 {
1604 return tempObject;
1605 }
1606 DateTime? tempDate = TryParseDate(tempString, readerOptions);
1607 if (tempDate != null)
1608 {
1609 return DataUtils.GetOADateTime(tempDate.Value);
1610 }
1611 TimeSpan? tempTime = TryParseTime(tempString, readerOptions);
1612 if (tempTime != null)
1613 {
1614 return DataUtils.GetOATime(tempTime.Value);
1615 }
1616 tempObject = ConvertToBool(raw, readerOptions);
1617 if (tempObject is bool)
1618 {
1619 return (bool)tempObject ? 1 : 0;
1620 }
1621 break;
1622 case Cell.CellType.Number:
1623 return raw;
1624 case Cell.CellType.Date:
1625 return DataUtils.GetOADateTime((DateTime)raw);
1626 case Cell.CellType.Time:
1627 return DataUtils.GetOATime((TimeSpan)raw);
1628 case Cell.CellType.Bool:
1629 if ((bool)raw)
1630 {
1631 return 1;
1632 }
1633 return 0;
1634 }
1635 return raw;
1636 }
1637
1638
1644 private static object GetNumericValue(string raw)
1645 {
1646 bool hasDecimalPoint = raw.Contains(".");
1647
1648 // Only try integer parsing if there's no decimal point
1649 if (!hasDecimalPoint)
1650 {
1651 // integer section (unchanged)
1652 uint uiValue;
1653 int iValue;
1654 bool canBeUint = ParserUtils.TryParseUint(raw, out uiValue);
1655 bool canBeInt = ParserUtils.TryParseInt(raw, out iValue);
1656 if (canBeUint && !canBeInt)
1657 {
1658 return uiValue;
1659 }
1660 else if (canBeInt)
1661 {
1662 return iValue;
1663 }
1664 ulong ulValue;
1665 long lValue;
1666 bool canBeUlong = ParserUtils.TryParseUlong(raw, out ulValue);
1667 bool canBeLong = ParserUtils.TryParseLong(raw, out lValue);
1668 if (canBeUlong && !canBeLong)
1669 {
1670 return ulValue;
1671 }
1672 else if (canBeLong)
1673 {
1674 return lValue;
1675 }
1676 }
1677
1678 decimal dcValue;
1679 double dValue;
1680 float fValue;
1681
1682 // Decimal/float section
1683 if (ParserUtils.TryParseDecimal(raw, out dcValue))
1684 {
1685 // Check if the value can be accurately represented as float
1686 float testFloat = decimal.ToSingle(dcValue);
1687 decimal backToDecimal = (decimal)testFloat;
1688
1689 // If converting to float and back preserves the value, use float
1690 if (dcValue == backToDecimal)
1691 {
1692 return testFloat;
1693 }
1694 else
1695 {
1696 // Otherwise use double for better precision
1697 return decimal.ToDouble(dcValue);
1698 }
1699 }
1700 // High range float section
1701 else if (ParserUtils.TryParseFloat(raw, out fValue) && fValue >= float.MinValue && fValue <= float.MaxValue && !float.IsInfinity(fValue))
1702 {
1703 return fValue;
1704 }
1705 if (ParserUtils.TryParseDouble(raw, out dValue))
1706 {
1707 return dValue;
1708 }
1709 return null;
1710 }
1711
1719 private static float GetValidatedWidth(float rawValue, ReaderOptions readerOptions)
1720 {
1721 if (rawValue < Worksheet.MinColumnWidth)
1722 {
1723 if (readerOptions.EnforceStrictValidation)
1724 {
1725 throw new WorksheetException($"The worksheet contains an invalid column width (too small: {rawValue}) value. This error is ignored when disabling the reader option 'EnforceStrictValidation'");
1726 }
1727 else
1728 {
1729 return Worksheet.MinColumnWidth;
1730 }
1731 }
1732 else if (rawValue > Worksheet.MaxColumnWidth)
1733 {
1734 if (readerOptions.EnforceStrictValidation)
1735 {
1736 throw new WorksheetException($"The worksheet contains an invalid column width (too large: {rawValue}) value. This error is ignored when disabling the reader option 'EnforceStrictValidation'");
1737 }
1738 else
1739 {
1740 return Worksheet.MaxColumnWidth;
1741 }
1742 }
1743 else
1744 {
1745 return rawValue;
1746 }
1747 }
1748
1756 private static float GetValidatedHeight(float rawValue, ReaderOptions readerOptions)
1757 {
1758 if (rawValue < Worksheet.MinRowHeight)
1759 {
1760 if (readerOptions.EnforceStrictValidation)
1761 {
1762 throw new WorksheetException($"The worksheet contains an invalid row height (too small: {rawValue}) value. Consider using the ImportOption 'EnforceValidRowDimensions' to ignore this error.");
1763 }
1764 else
1765 {
1766 return Worksheet.MinRowHeight;
1767 }
1768 }
1769 else if (rawValue > Worksheet.MaxRowHeight)
1770 {
1771 if (readerOptions.EnforceStrictValidation)
1772 {
1773 throw new WorksheetException($"The worksheet contains an invalid row height (too large: {rawValue}) value. Consider using the ImportOption 'EnforceValidRowDimensions' to ignore this error.");
1774 }
1775 else
1776 {
1777 return Worksheet.MaxRowHeight;
1778 }
1779 }
1780 else
1781 {
1782 return rawValue;
1783 }
1784 }
1785
1791 private string ResolveSharedString(string raw)
1792 {
1793 if (SharedStrings == null)
1794 {
1795 throw new IOException("The worksheet contains a shared-string cell, but no shared strings relationship was found for the workbook.");
1796 }
1797 int stringId;
1798 if (ParserUtils.TryParseInt(raw, out stringId))
1799 {
1800 string resolvedString = SharedStrings.ElementAtOrDefault(stringId);
1801 if (resolvedString == null)
1802 {
1803 return raw;
1804 }
1805 else
1806 {
1807 return resolvedString;
1808 }
1809 }
1810 return raw;
1811 }
1812
1821 private Cell CreateCell(object value, Cell.CellType type, Address address, string styleNumber = null)
1822 {
1823 Cell cell = new Cell(value, type, address);
1824 if (styleNumber != null && resolvedStyles.TryGetValue(styleNumber, out var styleValue))
1825 {
1826 cell.SetStyle(styleValue);
1827 }
1828 return cell;
1829 }
1830 #endregion
1831 }
1832}
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 shared strings. The index of the list corresponds to the index defined in ce...
int CurrentWorksheetID
Gets or sets the (r)ID of the current worksheet.
void Init(Stream stream, Workbook workbook, IOptions readerOptions, Action< Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
Initialization method (interface implementation).
Action< Stream, Workbook, string, IOptions, int?> InlinePluginHandler
Reference to the ReaderPlugInHandler, to be used for post operations in the Execute method.
WorksheetReader()
Default constructor - Must be defined for instantiation of the plug-ins.
virtual string DocumentType
Gets the relationship type URI of a worksheet document.
Workbook Workbook
Workbook reference where read data is stored (should not be null).
Class representing a collection of pre-processed styles and their components. This class is internall...
Exceptions.IOException IOException