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;
47 public virtual string DocumentType {
get {
return @"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"; } }
90 public void Init(Stream stream,
Workbook workbook, IOptions readerOptions, Action<Stream, Workbook, string, IOptions, int?> inlinePluginHandler)
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)
100 ProcessStyles(styleReaderContainer);
102 if (this.passwordReader ==
null)
104 this.passwordReader = PlugInLoader.GetPlugIn<IPasswordReader>(PlugInUUID.PasswordReader,
new LegacyPasswordReader());
105 this.passwordReader.Init(PasswordType.WorksheetProtection, (ReaderOptions)readerOptions);
117 WorksheetDefinition worksheetDefinition =
Workbook.AuxiliaryData.GetData<WorksheetDefinition>(PlugInUUID.WorkbookReader, PlugInUUID.WorksheetDefinitionEntity,
CurrentWorksheetID);
118 Worksheet worksheet =
new Worksheet(worksheetDefinition.WorksheetName, worksheetDefinition.SheetID,
Workbook)
120 Hidden = worksheetDefinition.Hidden
124 StringBuilder sb =
new StringBuilder();
125 using (XmlReader reader = XmlReader.Create(stream, XmlStreamUtils.CreateSettings()))
127 while (reader.Read())
129 if (reader.NodeType != XmlNodeType.Element)
133 switch (reader.LocalName.ToLowerInvariant())
136 GetSheetView(reader, worksheet);
138 case "sheetformatpr":
139 GetSheetFormats(reader, worksheet);
142 GetColumns(reader, worksheet, readerOptions);
145 GetRows(reader, worksheet, readerOptions, sb);
147 case "sheetprotection":
148 GetSheetProtection(reader, worksheet);
151 GetMergedCells(reader, worksheet);
154 GetAutoFilters(reader, worksheet);
158 SetWorkbookRelation(worksheet);
163 catch (NotSupportedContentException)
173 throw new IOException(
"The XML entry could not be read from the input stream. Please see the inner exception:", ex);
181 private void SetWorkbookRelation(Worksheet worksheet)
184 int selectedWorksheetId =
Workbook.AuxiliaryData.GetData<
int>(PlugInUUID.WorkbookReader, PlugInUUID.SelectedWorksheetEntity);
187 Workbook.SetSelectedWorksheet(worksheet);
195 private void ProcessStyles(StyleReaderContainer styleReaderContainer)
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++)
204 string index = ParserUtils.ToString(i);
205 Style style = styleReaderContainer.GetStyle(i, out isDate, out isTime);
208 this.dateStyles.Add(index);
212 this.timeStyles.Add(index);
214 this.resolvedStyles.Add(index, style);
225 private void GetRows(XmlReader reader, Worksheet worksheet, ReaderOptions readerOptions, StringBuilder sb)
227 using (XmlReader sheetDataReader = reader.ReadSubtree())
229 sheetDataReader.Read();
230 while (sheetDataReader.Read())
232 if (!XmlStreamUtils.IsElement(sheetDataReader,
"row"))
236 string rowAttribute = sheetDataReader.GetAttribute(
"r");
237 if (rowAttribute !=
null)
239 int rowNumber = ParserUtils.ParseInt(rowAttribute) - 1;
240 string hiddenAttribute = sheetDataReader.GetAttribute(
"hidden");
241 if (hiddenAttribute !=
null && ParserUtils.ParseBinaryBool(hiddenAttribute) == 1)
243 worksheet.AddHiddenRow(rowNumber);
245 string heightAttribute = sheetDataReader.GetAttribute(
"ht");
246 if (heightAttribute !=
null)
248 worksheet.RowHeights.Add(rowNumber, GetValidatedHeight(ParserUtils.ParseFloat(heightAttribute), readerOptions));
251 if (!sheetDataReader.IsEmptyElement)
253 using (XmlReader rowReader = sheetDataReader.ReadSubtree())
256 while (rowReader.Read())
258 if (!XmlStreamUtils.IsElement(rowReader,
"c"))
262 ReadCell(rowReader, worksheet, sb);
275 private static void GetSheetView(XmlReader reader, Worksheet worksheet)
277 using (XmlReader subtree = reader.ReadSubtree())
280 while (subtree.Read())
282 if (!XmlStreamUtils.IsElement(subtree,
"sheetView"))
286 string attribute = subtree.GetAttribute(
"view") ??
string.Empty;
287 worksheet.ViewType = Worksheet.GetSheetViewTypeEnum(attribute);
288 attribute = subtree.GetAttribute(
"zoomScale");
289 if (attribute !=
null)
291 worksheet.ZoomFactor = ParserUtils.ParseInt(attribute);
293 attribute = subtree.GetAttribute(
"zoomScaleNormal");
294 if (attribute !=
null)
296 worksheet.ZoomFactors[Worksheet.SheetViewType.Normal] = ParserUtils.ParseInt(attribute);
298 attribute = subtree.GetAttribute(
"zoomScalePageLayoutView");
299 if (attribute !=
null)
301 worksheet.ZoomFactors[Worksheet.SheetViewType.PageLayout] = ParserUtils.ParseInt(attribute);
303 attribute = subtree.GetAttribute(
"zoomScaleSheetLayoutView");
304 if (attribute !=
null)
306 worksheet.ZoomFactors[Worksheet.SheetViewType.PageBreakPreview] = ParserUtils.ParseInt(attribute);
308 attribute = subtree.GetAttribute(
"showGridLines");
309 if (attribute !=
null)
311 worksheet.ShowGridLines = ParserUtils.ParseBinaryBool(attribute) == 1;
313 attribute = subtree.GetAttribute(
"showRowColHeaders");
314 if (attribute !=
null)
316 worksheet.ShowRowColumnHeaders = ParserUtils.ParseBinaryBool(attribute) == 1;
318 attribute = subtree.GetAttribute(
"showRuler");
319 if (attribute !=
null)
321 worksheet.ShowRuler = ParserUtils.ParseBinaryBool(attribute) == 1;
323 using (XmlReader sheetViewReader = subtree.ReadSubtree())
325 sheetViewReader.Read();
326 while (sheetViewReader.Read())
328 if (sheetViewReader.NodeType != XmlNodeType.Element)
332 if (XmlStreamUtils.IsElement(sheetViewReader,
"selection"))
334 attribute = sheetViewReader.GetAttribute(
"sqref");
335 if (attribute !=
null)
337 if (attribute.Contains(
" "))
339 string[] ranges = attribute.Split(
' ');
340 foreach (
string range
in ranges)
342 CollectSelectedCells(range, worksheet);
347 CollectSelectedCells(attribute, worksheet);
351 else if (XmlStreamUtils.IsElement(sheetViewReader,
"pane"))
353 SetPaneSplit(sheetViewReader, worksheet);
366 private static void CollectSelectedCells(
string attribute, Worksheet worksheet)
368 if (attribute.Contains(
":"))
371 worksheet.AddSelectedCells(
new Range(attribute));
376 worksheet.AddSelectedCells(
new Range(attribute +
":" + attribute));
385 private static void SetPaneSplit(XmlReader reader, Worksheet worksheet)
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);
397 Worksheet.WorksheetPane? activePane =
null;
398 if (attribute !=
null)
400 if (ParserUtils.ToLower(attribute) ==
"frozen" || ParserUtils.ToLower(attribute) ==
"frozensplit")
404 useNumbers = frozenState;
406 attribute = reader.GetAttribute(
"ySplit");
407 if (attribute !=
null)
409 ySplitDefined =
true;
412 paneSplitRowIndex = ParserUtils.ParseInt(attribute);
416 paneSplitHeight = DataUtils.GetPaneSplitHeight(ParserUtils.ParseFloat(attribute));
419 attribute = reader.GetAttribute(
"xSplit");
420 if (attribute !=
null)
422 xSplitDefined =
true;
425 paneSplitColumnIndex = ParserUtils.ParseInt(attribute);
429 paneSplitWidth = DataUtils.GetPaneSplitWidth(ParserUtils.ParseFloat(attribute));
432 attribute = reader.GetAttribute(
"topLeftCell");
433 if (attribute !=
null)
435 topLeftCell =
new Address(attribute);
437 attribute = reader.GetAttribute(
"activePane") ??
string.Empty;
438 activePane = Worksheet.GetWorksheetPaneEnum(attribute);
441 if (ySplitDefined && !xSplitDefined)
443 worksheet.SetHorizontalSplit(paneSplitRowIndex.Value, frozenState, topLeftCell, activePane);
445 if (!ySplitDefined && xSplitDefined)
447 worksheet.SetVerticalSplit(paneSplitColumnIndex.Value, frozenState, topLeftCell, activePane);
449 else if (ySplitDefined && xSplitDefined)
451 worksheet.SetSplit(paneSplitColumnIndex.Value, paneSplitRowIndex.Value, frozenState, topLeftCell, activePane);
456 if (ySplitDefined && !xSplitDefined)
458 worksheet.SetHorizontalSplit(paneSplitHeight.Value, topLeftCell, activePane);
460 if (!ySplitDefined && xSplitDefined)
462 worksheet.SetVerticalSplit(paneSplitWidth.Value, topLeftCell, activePane);
464 else if (ySplitDefined && xSplitDefined)
466 worksheet.SetSplit(paneSplitWidth, paneSplitHeight, topLeftCell, activePane);
479 private void GetSheetProtection(XmlReader reader, Worksheet worksheet)
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)
499 worksheet.UseSheetProtection =
true;
502 using (XmlReader subtree = reader.ReadSubtree())
504 subtree.MoveToContent();
505 outerXml = subtree.ReadOuterXml();
507 XmlDocument miniDoc =
new XmlDocument { XmlResolver =
null };
508 miniDoc.LoadXml(outerXml);
509 this.passwordReader.ReadXmlAttributes(miniDoc.DocumentElement);
510 if (this.passwordReader.PasswordIsSet())
512 if (this.passwordReader is LegacyPasswordReader && (this.passwordReader as LegacyPasswordReader).ContemporaryAlgorithmDetected && (readerOptions ==
null || !readerOptions.IgnoreNotSupportedPasswordAlgorithms))
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");
516 worksheet.SheetProtectionPassword.CopyFrom(this.passwordReader);
527 private static int ReadSheetProtectionAttribute(XmlReader reader, Worksheet.SheetProtectionValue sheetProtectionValue, Worksheet worksheet)
529 string attrName = Worksheet.GetSheetProtectionName(sheetProtectionValue);
530 if (reader.GetAttribute(attrName) !=
null)
532 worksheet.SheetProtectionValues.Add(sheetProtectionValue);
543 private static void GetMergedCells(XmlReader reader, Worksheet worksheet)
545 using (XmlReader subtree = reader.ReadSubtree())
548 while (subtree.Read())
550 if (!XmlStreamUtils.IsElement(subtree,
"mergeCell"))
554 string attribute = subtree.GetAttribute(
"ref");
555 if (attribute !=
null)
557 worksheet.MergeCells(
new Range(attribute));
568 private static void GetSheetFormats(XmlReader reader, Worksheet worksheet)
570 string attribute = reader.GetAttribute(
"defaultColWidth");
571 if (attribute !=
null)
573 worksheet.DefaultColumnWidth = ParserUtils.ParseFloat(attribute);
575 attribute = reader.GetAttribute(
"defaultRowHeight");
576 if (attribute !=
null)
578 worksheet.DefaultRowHeight = ParserUtils.ParseFloat(attribute);
587 private static void GetAutoFilters(XmlReader reader, Worksheet worksheet)
589 string autoFilterRef = reader.GetAttribute(
"ref");
590 if (autoFilterRef !=
null)
592 Range range =
new Range(autoFilterRef);
593 worksheet.SetAutoFilter(range.StartAddress.Column, range.EndAddress.Column);
603 private void GetColumns(XmlReader reader, Worksheet worksheet, ReaderOptions readerOptions)
605 using (XmlReader subtree = reader.ReadSubtree())
608 while (subtree.Read())
610 if (!XmlStreamUtils.IsElement(subtree,
"col"))
616 List<int> indices =
new List<int>();
617 string attribute = subtree.GetAttribute(
"min");
618 if (attribute !=
null)
620 min = ParserUtils.ParseInt(attribute);
622 indices.Add(min.Value);
624 attribute = subtree.GetAttribute(
"max");
625 if (attribute !=
null)
627 max = ParserUtils.ParseInt(attribute);
629 if (min !=
null && max.Value != min.Value)
631 for (
int i = min.Value; i <= max.Value; i++)
636 attribute = subtree.GetAttribute(
"width");
637 float width = Worksheet.DefaultWorksheetColumnWidth;
638 if (attribute !=
null)
640 width = ParserUtils.ParseFloat(attribute);
642 attribute = subtree.GetAttribute(
"hidden");
644 if (attribute !=
null && ParserUtils.ParseBinaryBool(attribute) == 1)
648 attribute = subtree.GetAttribute(
"style");
649 Style defaultStyle =
null;
650 if (attribute !=
null && resolvedStyles.TryGetValue(attribute, out var attributeValue))
652 defaultStyle = attributeValue;
654 foreach (
int index
in indices)
656 string columnAddress = Cell.ResolveColumnAddress(index - 1);
657 if (defaultStyle !=
null)
659 worksheet.SetColumnDefaultStyle(columnAddress, defaultStyle);
661 if (width != Worksheet.DefaultWorksheetColumnWidth)
663 worksheet.SetColumnWidth(columnAddress, GetValidatedWidth(width, readerOptions));
667 worksheet.AddHiddenColumn(columnAddress);
680 private void ReadCell(XmlReader rowReader, Worksheet worksheet, StringBuilder sb)
682 string address = rowReader.GetAttribute(
"r");
683 string type = rowReader.GetAttribute(
"t");
684 string styleNumber = rowReader.GetAttribute(
"s");
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)
695 using (XmlReader cellReader = rowReader.ReadSubtree())
698 while (cellReader.Read())
700 if (cellReader.NodeType != XmlNodeType.Element)
704 if (cellReader.LocalName.Equals(
"f", StringComparison.OrdinalIgnoreCase))
706 formulaType = cellReader.GetAttribute(
"t");
707 formulaReference = cellReader.GetAttribute(
"ref");
708 formulaExpression = cellReader.ReadElementContentAsString();
711 if (cellReader.LocalName.Equals(
"v", StringComparison.OrdinalIgnoreCase))
715 cachedValue = cellReader.ReadElementContentAsString();
716 hasCachedValue =
true;
720 value = cellReader.ReadElementContentAsString();
723 else if (cellReader.LocalName.Equals(
"is", StringComparison.OrdinalIgnoreCase))
726 using (XmlReader isReader = cellReader.ReadSubtree())
729 while (isReader.Read())
731 if (isReader.NodeType == XmlNodeType.Element &&
732 isReader.LocalName.Equals(
"t", StringComparison.OrdinalIgnoreCase))
734 sb.Append(isReader.ReadElementContentAsString());
738 value = sb.ToString();
739 hasInlineString =
true;
746 if (type ==
"s" && hasCachedValue)
748 cachedValue = ResolveSharedString(cachedValue)?.ToString();
750 else if (type ==
"inlineStr" && hasInlineString)
753 hasCachedValue =
true;
755 value = formulaExpression;
757 else if (type ==
"str" && !hasFormula)
761 hasCachedValue =
true;
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);
779 private Cell ResolveCellData(
string raw,
string type,
string styleNumber,
string address)
781 Cell.CellType importedType = Cell.CellType.Default;
785 importedType = Cell.CellType.Error;
786 if (!Errors.TryParseFormulaError(raw, out Errors.FormulaError error))
788 error = Errors.FormulaError.UnknownError;
792 else if (type ==
"b")
794 rawValue = TryParseBool(raw);
795 if (rawValue !=
null)
797 importedType = Cell.CellType.Bool;
801 rawValue = GetNumericValue(raw);
802 if (rawValue !=
null)
804 importedType = Cell.CellType.Number;
808 else if (type ==
"s")
810 importedType = Cell.CellType.String;
811 rawValue = ResolveSharedString(raw);
813 else if (type ==
"inlineStr")
815 importedType = Cell.CellType.String;
818 else if (dateStyles.Contains(styleNumber) && (type ==
null || type ==
"" || type ==
"n"))
820 rawValue = GetDateTimeValue(raw, Cell.CellType.Date, out importedType);
822 else if (timeStyles.Contains(styleNumber) && (type ==
null || type ==
"" || type ==
"n"))
824 rawValue = GetDateTimeValue(raw, Cell.CellType.Time, out importedType);
828 importedType = Cell.CellType.Number;
829 rawValue = GetNumericValue(raw);
831 if (rawValue ==
null && raw ==
"")
833 importedType = Cell.CellType.Empty;
836 else if (rawValue ==
null && raw.Length > 0)
838 importedType = Cell.CellType.String;
841 Address cellAddress =
new Address(address);
842 if (readerOptions !=
null)
844 if (readerOptions.EnforcedColumnTypes.Count > 0)
846 rawValue = GetEnforcedColumnValue(rawValue, importedType, cellAddress);
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)
854 rawValue = ((DateTime)rawValue).AddDays(1);
857 return CreateCell(rawValue, importedType, cellAddress, styleNumber);
870 private static void UpsertFormulaData(Cell cell,
string expression,
string cachedValue,
bool hasCachedValue,
string cachedValueType,
string formulaType,
string formulaReference)
872 if (cell.DataType != Cell.CellType.Formula)
876 FormulaData formula = cell.Formula;
877 Cell.CellType resolvedCachedValueType = ResolveFormulaCachedValueType(cachedValueType, hasCachedValue);
878 formula.Expression = expression;
879 if (resolvedCachedValueType == Cell.CellType.Error)
881 if (!Errors.TryParseFormulaError(cachedValue, out Errors.FormulaError error))
883 error = Errors.FormulaError.UnknownError;
885 formula.CachedValue = error;
889 formula.CachedValue = cachedValue;
891 formula.CachedValueType = resolvedCachedValueType;
892 formula.FormulaRange = formulaReference;
893 if (!
string.IsNullOrEmpty(formulaType))
898 formula.Type = FormulaData.FormulaType.Array;
901 formula.Type = FormulaData.FormulaType.DataTable;
904 formula.Type = FormulaData.FormulaType.Shared;
907 formula.Type = FormulaData.FormulaType.Normal;
913 formula.Type = FormulaData.FormulaType.Normal;
916 cell.Formula = formula;
925 private static Cell.CellType ResolveFormulaCachedValueType(
string type,
bool hasCachedValue)
929 return Cell.CellType.Default;
936 return Cell.CellType.Number;
940 return Cell.CellType.String;
942 return Cell.CellType.Bool;
944 return Cell.CellType.Error;
946 return Cell.CellType.Date;
948 return Cell.CellType.Default;
957 private static Cell.CellType ResolveType(
object value)
961 return Cell.CellType.Empty;
976 return Cell.CellType.Number;
978 return Cell.CellType.Date;
980 return Cell.CellType.Time;
982 return Cell.CellType.Bool;
983 case Errors.FormulaError _:
984 return Cell.CellType.Error;
986 return Cell.CellType.String;
996 private object GetGloballyEnforcedFlagValues(
object data, Address address)
998 if (address.Row < readerOptions.EnforcingStartRowNumber)
1002 if (readerOptions.EnforceDateTimesAsNumbers)
1004 if (data is DateTime)
1006 data = DataUtils.GetOADateTime((DateTime)data,
true);
1008 else if (data is TimeSpan)
1010 data = DataUtils.GetOATime((TimeSpan)data);
1013 if (readerOptions.EnforceEmptyValuesAsString && data ==
null)
1026 private object GetGloballyEnforcedValue(
object data, Address address)
1028 if (address.Row < readerOptions.EnforcingStartRowNumber)
1032 if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToDouble)
1034 object tempDouble = ConvertToDouble(data, readerOptions);
1035 if (tempDouble !=
null)
1040 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToDecimal)
1042 object tempDecimal = ConvertToDecimal(data, readerOptions);
1043 if (tempDecimal !=
null)
1048 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.AllNumbersToInt)
1050 object tempInt = ConvertToInt(data);
1051 if (tempInt !=
null)
1056 else if (readerOptions.GlobalEnforcingType == ReaderOptions.GlobalType.EverythingToString)
1058 return ConvertToString(data, readerOptions);
1070 private object GetEnforcedColumnValue(
object data, Cell.CellType importedTyp, Address address)
1072 if (address.Row < readerOptions.EnforcingStartRowNumber)
1076 if (!readerOptions.EnforcedColumnTypes.TryGetValue(address.Column, out var columnType))
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);
1095 return ConvertToString(data, readerOptions);
1105 private object ConvertToBool(
object data, ReaderOptions readerOptions)
1120 object tempObject = ConvertToDouble(data, readerOptions);
1121 if (tempObject is
double)
1123 double tempDouble = (double)tempObject;
1124 if (
double.Equals(tempDouble, 0d))
1128 else if (
double.Equals(tempDouble, 1d))
1136 string tempString = (string)data;
1137 bool? tempBool = TryParseBool(tempString);
1138 if (tempBool !=
null)
1140 return tempBool.Value;
1152 private static bool? TryParseBool(
string raw)
1158 else if (raw ==
"1")
1165 if (
bool.TryParse(raw, out value))
1182 private object ConvertToDouble(
object data, ReaderOptions readerOptions)
1184 object value = ConvertToDecimal(data, readerOptions);
1185 if (value is decimal)
1187 return Decimal.ToDouble((decimal)value);
1189 else if (value is
float)
1191 return Convert.ToDouble((
float)value);
1202 private object ConvertToDecimal(
object data, ReaderOptions readerOptions)
1204 IConvertible converter;
1207 case double doubleValue:
1210 return Convert.ToDecimal(doubleValue);
1212 catch (OverflowException)
1225 converter = data as IConvertible;
1226 double tempDouble = converter.ToDouble(DataUtils.InvariantCulture);
1227 if (tempDouble > (
double)decimal.MaxValue || tempDouble < (
double)decimal.MinValue)
1233 return converter.ToDecimal(DataUtils.InvariantCulture);
1242 return decimal.Zero;
1245 return new decimal(DataUtils.GetOADateTime((DateTime)data));
1247 return new decimal(DataUtils.GetOATime((TimeSpan)data));
1250 string tempString = (string)data;
1251 if (ParserUtils.TryParseDecimal(tempString, out dValue))
1255 DateTime? tempDate = TryParseDate(tempString, readerOptions);
1256 if (tempDate !=
null)
1258 return new decimal(DataUtils.GetOADateTime(tempDate.Value));
1260 TimeSpan? tempTime = TryParseTime(tempString, readerOptions);
1261 if (tempTime !=
null)
1263 return new decimal(DataUtils.GetOATime(tempTime.Value));
1275 private static object ConvertToInt(
object data)
1285 tempDouble = DataUtils.GetOADateTime((DateTime)data,
true);
1286 return ConvertDoubleToInt(tempDouble);
1288 tempDouble = DataUtils.GetOATime((TimeSpan)data);
1289 return ConvertDoubleToInt(tempDouble);
1292 int? tempInt = TryConvertDoubleToInt(data);
1293 if (tempInt !=
null)
1299 return (
bool)data ? 1 : 0;
1302 if (ParserUtils.TryParseInt((
string)data, out tempInt2))
1317 private object ConvertToDate(
object data, ReaderOptions readerOptions)
1324 DateTime root = DataUtils.FirstAllowedExcelDate;
1325 TimeSpan time = (TimeSpan)data;
1326 root = root.AddDays(-1);
1327 root = root.AddHours(time.Hours);
1328 root = root.AddMinutes(time.Minutes);
1329 root = root.AddSeconds(time.Seconds);
1341 return ConvertDateFromDouble(data, readerOptions);
1343 DateTime? date2 = TryParseDate((
string)data, readerOptions);
1348 return ConvertDateFromDouble(data, readerOptions);
1359 private DateTime? TryParseDate(
string raw, ReaderOptions readerOptions)
1363 if (readerOptions ==
null ||
string.IsNullOrEmpty(readerOptions.DateTimeFormat) || readerOptions.TemporalCultureInfo ==
null)
1365 isDateTime = DateTime.TryParse(raw, ReaderOptions.DefaultCultureInfo, DateTimeStyles.None, out dateTime);
1369 isDateTime = DateTime.TryParseExact(raw, readerOptions.DateTimeFormat, readerOptions.TemporalCultureInfo, DateTimeStyles.None, out dateTime);
1371 if (isDateTime && dateTime >= DataUtils.FirstAllowedExcelDate && dateTime <= DataUtils.LastAllowedExcelDate)
1384 private object ConvertToTime(
object data, ReaderOptions readerOptions)
1389 return ConvertTimeFromDouble(data, readerOptions);
1402 return ConvertTimeFromDouble(data, readerOptions);
1404 TimeSpan? time = TryParseTime((
string)data, readerOptions);
1409 return ConvertTimeFromDouble(data, readerOptions);
1420 private static TimeSpan? TryParseTime(
string raw, ReaderOptions readerOptions)
1424 if (readerOptions ==
null ||
string.IsNullOrEmpty(readerOptions.TimeSpanFormat) || readerOptions.TemporalCultureInfo ==
null)
1426 isTimeSpan = TimeSpan.TryParse(raw, ReaderOptions.DefaultCultureInfo, out timeSpan);
1430 isTimeSpan = TimeSpan.TryParseExact(raw, readerOptions.TimeSpanFormat, readerOptions.TemporalCultureInfo, out timeSpan);
1432 if (isTimeSpan && timeSpan.Days >= 0 && timeSpan.Days < DataUtils.MaxOADateValue)
1447 private static object GetDateTimeValue(
string raw, Cell.CellType valueType, out Cell.CellType resolvedType)
1450 if (!ParserUtils.TryParseDouble(raw, out dValue))
1452 resolvedType = Cell.CellType.String;
1455 if ((valueType == Cell.CellType.Date && (dValue < DataUtils.MinOADateValue || dValue > DataUtils.MaxOADateValue)) || (valueType == Cell.CellType.Time && (dValue < 0.0 || dValue > DataUtils.MaxOADateValue)))
1458 resolvedType = Cell.CellType.Number;
1459 return GetNumericValue(raw);
1461 DateTime tempDate = DataUtils.GetDateFromOA(dValue);
1464 tempDate = tempDate.AddDays(1);
1466 if (valueType == Cell.CellType.Date)
1468 resolvedType = Cell.CellType.Date;
1473 resolvedType = Cell.CellType.Time;
1474 return new TimeSpan((
int)dValue, tempDate.Hour, tempDate.Minute, tempDate.Second);
1484 private object ConvertDateFromDouble(
object data, ReaderOptions readerOptions)
1486 object oaDate = ConvertToDouble(data, readerOptions);
1487 if (oaDate is
double && (
double)oaDate < DataUtils.MaxOADateValue)
1489 DateTime date = DataUtils.GetDateFromOA((
double)oaDate);
1490 if (date >= DataUtils.FirstAllowedExcelDate && date <= DataUtils.LastAllowedExcelDate)
1504 private object ConvertTimeFromDouble(
object data, ReaderOptions readerOptions)
1506 object oaDate = ConvertToDouble(data, readerOptions);
1507 if (oaDate is
double)
1509 double d = (double)oaDate;
1510 if (d >= DataUtils.MinOADateValue && d <= DataUtils.MaxOADateValue)
1512 DateTime date = DataUtils.GetDateFromOA(d);
1513 return new TimeSpan((
int)d, date.Hour, date.Minute, date.Second);
1524 private static int? TryConvertDoubleToInt(
object data)
1526 IConvertible converter = data as IConvertible;
1527 double dValue = converter.ToDouble(ReaderOptions.DefaultCultureInfo);
1528 if (dValue >
int.MinValue && dValue <
int.MaxValue)
1530 return converter.ToInt32(ReaderOptions.DefaultCultureInfo);
1540 private static int ConvertDoubleToInt(
object data)
1542 IConvertible converter = data as IConvertible;
1543 return converter.ToInt32(ReaderOptions.DefaultCultureInfo);
1552 private static string ConvertToString(
object data, ReaderOptions readerOptions)
1557 return ((
int)data).ToString(ReaderOptions.DefaultCultureInfo);
1559 return ((uint)data).ToString(ReaderOptions.DefaultCultureInfo);
1561 return ((
long)data).ToString(ReaderOptions.DefaultCultureInfo);
1563 return ((ulong)data).ToString(ReaderOptions.DefaultCultureInfo);
1565 return ((
float)data).ToString(ReaderOptions.DefaultCultureInfo);
1567 return ((
double)data).ToString(ReaderOptions.DefaultCultureInfo);
1569 return ((
bool)data).ToString(ReaderOptions.DefaultCultureInfo);
1571 return ((DateTime)data).ToString(readerOptions.DateTimeFormat, ParserUtils.InvariantCulture);
1573 return ((TimeSpan)data).ToString(readerOptions.TimeSpanFormat, ParserUtils.InvariantCulture);
1579 return data.ToString();
1590 private object GetNumericValue(
object raw, Cell.CellType importedType, ReaderOptions readerOptions)
1597 switch (importedType)
1599 case Cell.CellType.String:
1600 string tempString = raw.ToString();
1601 tempObject = GetNumericValue(tempString);
1602 if (tempObject !=
null)
1606 DateTime? tempDate = TryParseDate(tempString, readerOptions);
1607 if (tempDate !=
null)
1609 return DataUtils.GetOADateTime(tempDate.Value);
1611 TimeSpan? tempTime = TryParseTime(tempString, readerOptions);
1612 if (tempTime !=
null)
1614 return DataUtils.GetOATime(tempTime.Value);
1616 tempObject = ConvertToBool(raw, readerOptions);
1617 if (tempObject is
bool)
1619 return (
bool)tempObject ? 1 : 0;
1622 case Cell.CellType.Number:
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:
1644 private static object GetNumericValue(
string raw)
1646 bool hasDecimalPoint = raw.Contains(
".");
1649 if (!hasDecimalPoint)
1654 bool canBeUint = ParserUtils.TryParseUint(raw, out uiValue);
1655 bool canBeInt = ParserUtils.TryParseInt(raw, out iValue);
1656 if (canBeUint && !canBeInt)
1666 bool canBeUlong = ParserUtils.TryParseUlong(raw, out ulValue);
1667 bool canBeLong = ParserUtils.TryParseLong(raw, out lValue);
1668 if (canBeUlong && !canBeLong)
1683 if (ParserUtils.TryParseDecimal(raw, out dcValue))
1686 float testFloat = decimal.ToSingle(dcValue);
1687 decimal backToDecimal = (decimal)testFloat;
1690 if (dcValue == backToDecimal)
1697 return decimal.ToDouble(dcValue);
1701 else if (ParserUtils.TryParseFloat(raw, out fValue) && fValue >=
float.MinValue && fValue <=
float.MaxValue && !
float.IsInfinity(fValue))
1705 if (ParserUtils.TryParseDouble(raw, out dValue))
1719 private static float GetValidatedWidth(
float rawValue, ReaderOptions readerOptions)
1721 if (rawValue < Worksheet.MinColumnWidth)
1723 if (readerOptions.EnforceStrictValidation)
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'");
1729 return Worksheet.MinColumnWidth;
1732 else if (rawValue > Worksheet.MaxColumnWidth)
1734 if (readerOptions.EnforceStrictValidation)
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'");
1740 return Worksheet.MaxColumnWidth;
1756 private static float GetValidatedHeight(
float rawValue, ReaderOptions readerOptions)
1758 if (rawValue < Worksheet.MinRowHeight)
1760 if (readerOptions.EnforceStrictValidation)
1762 throw new WorksheetException($
"The worksheet contains an invalid row height (too small: {rawValue}) value. Consider using the ImportOption 'EnforceValidRowDimensions' to ignore this error.");
1766 return Worksheet.MinRowHeight;
1769 else if (rawValue > Worksheet.MaxRowHeight)
1771 if (readerOptions.EnforceStrictValidation)
1773 throw new WorksheetException($
"The worksheet contains an invalid row height (too large: {rawValue}) value. Consider using the ImportOption 'EnforceValidRowDimensions' to ignore this error.");
1777 return Worksheet.MaxRowHeight;
1791 private string ResolveSharedString(
string raw)
1795 throw new IOException(
"The worksheet contains a shared-string cell, but no shared strings relationship was found for the workbook.");
1798 if (ParserUtils.TryParseInt(raw, out stringId))
1800 string resolvedString =
SharedStrings.ElementAtOrDefault(stringId);
1801 if (resolvedString ==
null)
1807 return resolvedString;
1821 private Cell CreateCell(
object value, Cell.CellType type, Address address,
string styleNumber =
null)
1823 Cell cell =
new Cell(value, type, address);
1824 if (styleNumber !=
null && resolvedStyles.TryGetValue(styleNumber, out var styleValue))
1826 cell.SetStyle(styleValue);