9using NanoXLSX.Interfaces;
10using NanoXLSX.Interfaces.Writer;
12using NanoXLSX.Registry;
13using NanoXLSX.Registry.Attributes;
15using NanoXLSX.Utils.Xml;
17using System.Collections.Generic;
18using System.Globalization;
28 [NanoXlsxPlugIn(PlugInUUID = PlugInUUID.WorksheetWriter)]
29 internal class WorksheetWriter : IWorksheetWriter
31 private XmlElement worksheet;
32 private Worksheet currentWorksheet;
33 private IPasswordWriter passwordWriter;
34 private ISortedMap sharedStrings;
35 private ISharedStringWriter sharedStringWriter;
41 public Workbook Workbook {
get;
set; }
46 public Worksheet CurrentWorksheet
48 get => currentWorksheet;
51 currentWorksheet = value;
52 IPassword passwordInstance = ((Worksheet)CurrentWorksheet).SheetProtectionPassword;
53 this.passwordWriter = PlugInLoader.GetPlugIn<IPasswordWriter>(PlugInUUID.PasswordWriter,
new LegacyPasswordWriter());
54 this.passwordWriter.Init(PasswordType.WorksheetProtection, passwordInstance.PasswordHash);
61 public XmlElement XmlElement => worksheet;
69 internal WorksheetWriter()
79 public void Init(IBaseWriter baseWriter)
81 this.Workbook = baseWriter.Workbook;
82 this.sharedStringWriter = baseWriter.SharedStringWriter;
83 this.sharedStrings = this.sharedStringWriter.SharedStrings;
91 Worksheet ws = currentWorksheet;
92 ws.RecalculateAutoFilter();
93 ws.RecalculateColumns();
94 worksheet = XmlElement.CreateElement(
"worksheet");
95 worksheet.AddDefaultXmlNameSpace(
"http://schemas.openxmlformats.org/spreadsheetml/2006/main");
96 worksheet.AddNameSpaceAttribute(
"mc",
"xmlns",
"http://schemas.openxmlformats.org/markup-compatibility/2006");
97 worksheet.AddNameSpaceAttribute(
"x14ac",
"xmlns",
"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
98 worksheet.AddAttribute(
"mc:Ignorable",
"x14ac");
99 if (ws.GetLastCellAddress().HasValue && ws.GetFirstCellAddress().HasValue)
101 worksheet.AddChildElementWithAttribute(
"dimension",
"ref",
new Range(ws.GetFirstCellAddress().Value, ws.GetLastCellAddress().Value).ToString());
103 if (ws.SelectedCells.Count > 0 || ws.PaneSplitTopHeight !=
null || ws.PaneSplitLeftWidth !=
null || ws.PaneSplitAddress !=
null ||
104 ws.Hidden || ws.ZoomFactor != 100 || ws.ZoomFactors.Count > 1 || !ws.ShowGridLines || !ws.ShowRuler || !ws.ShowRowColumnHeaders || ws.ViewType != Worksheet.SheetViewType.Normal)
106 worksheet.AddChildElement(CreateSheetViewElement(ws));
108 XmlElement sheetFormatPr = worksheet.AddChildElement(
"sheetFormatPr");
109 if (!HasPaneSplitting(ws))
112 sheetFormatPr.AddAttribute(
"defaultColWidth", ParserUtils.ToString(ws.DefaultColumnWidth));
114 sheetFormatPr.AddAttribute(
"defaultRowHeight", ParserUtils.ToString(ws.DefaultRowHeight));
115 sheetFormatPr.AddAttribute(
"baseColWidth", ParserUtils.ToString(ws.DefaultColumnWidth));
116 sheetFormatPr.AddAttribute(
"dyDescent",
"0.25",
"x14ac");
118 worksheet.AddChildElement(CreateColsElement(ws));
120 XmlElement sheetData = worksheet.AddChildElement(
"sheetData");
121 sheetData.AddChildElements(CreateRowElements(ws));
123 worksheet.AddChildElement(CreateMergedCellsElement(ws));
124 worksheet.AddChildElement(CreateSheetProtectionElement(ws));
125 if (ws.AutoFilterRange !=
null)
127 worksheet.AddChildElementWithAttribute(
"autoFilter",
"ref", ws.AutoFilterRange.Value.ToString());
130 WriterPlugInHandler.HandleInlineQueuePlugins(ref worksheet, Workbook, PlugInUUID.WorksheetInlineWriter, currentWorksheet.SheetID);
136 void IWorksheetWriter.ReleaseXmlElement()
138 this.worksheet =
null;
146 private static XmlElement CreateMergedCellsElement(Worksheet worksheet)
148 if (worksheet.MergedCells.Count < 1)
152 XmlElement mergeCells = XmlElement.CreateElementWithAttribute(
"mergeCells",
"count", ParserUtils.ToString(worksheet.MergedCells.Count));
153 foreach (KeyValuePair<string, Range> item
in worksheet.MergedCells)
155 mergeCells.AddChildElementWithAttribute(
"mergeCell",
"ref", item.Value.ToString());
165 private XmlElement CreateSheetViewElement(Worksheet worksheet)
167 XmlElement sheetViews = XmlElement.CreateElement(
"sheetViews");
168 XmlElement sheetView = sheetViews.AddChildElementWithAttribute(
"sheetView",
"workbookViewId",
"0");
169 if (Workbook.SelectedWorksheet == worksheet.SheetID - 1 && !worksheet.Hidden)
171 sheetView.AddAttribute(
"tabSelected",
"1");
173 if (worksheet.ViewType != Worksheet.SheetViewType.Normal)
175 if (worksheet.ViewType == Worksheet.SheetViewType.PageLayout)
177 if (worksheet.ShowRuler)
179 sheetView.AddAttribute(
"showRuler",
"1");
183 sheetView.AddAttribute(
"showRuler",
"0");
185 sheetView.AddAttribute(
"view",
"pageLayout");
187 else if (worksheet.ViewType == Worksheet.SheetViewType.PageBreakPreview)
189 sheetView.AddAttribute(
"view",
"pageBreakPreview");
192 if (!worksheet.ShowGridLines)
194 sheetView.AddAttribute(
"showGridLines",
"0");
196 if (!worksheet.ShowRowColumnHeaders)
198 sheetView.AddAttribute(
"showRowColHeaders",
"0");
200 sheetView.AddAttribute(
"zoomScale", ParserUtils.ToString(worksheet.ZoomFactor));
201 foreach (KeyValuePair<Worksheet.SheetViewType, int> scaleFactor
in worksheet.ZoomFactors)
203 if (scaleFactor.Key == worksheet.ViewType)
207 if (scaleFactor.Key == Worksheet.SheetViewType.Normal)
209 sheetView.AddAttribute(
"zoomScaleNormal", ParserUtils.ToString(scaleFactor.Value));
211 else if (scaleFactor.Key == Worksheet.SheetViewType.PageBreakPreview)
213 sheetView.AddAttribute(
"zoomScaleSheetLayoutView", ParserUtils.ToString(scaleFactor.Value));
215 else if (scaleFactor.Key == Worksheet.SheetViewType.PageLayout)
217 sheetView.AddAttribute(
"zoomScalePageLayoutView", ParserUtils.ToString(scaleFactor.Value));
220 sheetView.AddChildElements(CreatePaneElements(worksheet));
221 if (worksheet.SelectedCells.Count > 0)
223 XmlElement selection = sheetView.AddChildElement(
"selection");
224 selection.AddAttribute(
"activeCell", worksheet.SelectedCells[0].StartAddress.ToString());
225 StringBuilder sb =
new StringBuilder(worksheet.SelectedCells.Count * 4);
226 for (
int i = 0; i < worksheet.SelectedCells.Count; i++)
228 sb.Append(worksheet.SelectedCells[i].ToString());
229 if (i < worksheet.SelectedCells.Count - 1)
234 selection.AddAttribute(
"sqref", sb.ToString());
244 private static bool HasPaneSplitting(Worksheet worksheet)
246 if (worksheet.PaneSplitLeftWidth ==
null && worksheet.PaneSplitTopHeight ==
null && worksheet.PaneSplitAddress ==
null)
258 private XmlElement CreateSheetProtectionElement(Worksheet worksheet)
260 if (!worksheet.UseSheetProtection)
264 Dictionary<Worksheet.SheetProtectionValue,
int> actualLockingValues =
new Dictionary<Worksheet.SheetProtectionValue,
int>();
265 if (!worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Objects))
267 actualLockingValues.Add(Worksheet.SheetProtectionValue.Objects, 1);
269 if (!worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Scenarios))
271 actualLockingValues.Add(Worksheet.SheetProtectionValue.Scenarios, 1);
273 bool allowSelectLocked = worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.SelectLockedCells);
274 bool allowSelectUnlocked = worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.SelectUnlockedCells);
275 if (allowSelectLocked && !allowSelectUnlocked)
278 allowSelectUnlocked =
true;
280 if (!allowSelectLocked)
282 actualLockingValues.Add(Worksheet.SheetProtectionValue.SelectLockedCells, 1);
284 if (!allowSelectUnlocked)
286 actualLockingValues.Add(Worksheet.SheetProtectionValue.SelectUnlockedCells, 1);
289 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatCells))
291 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatCells, 0);
293 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatColumns))
295 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatColumns, 0);
297 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatRows))
299 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatRows, 0);
301 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertColumns))
303 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertColumns, 0);
305 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertRows))
307 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertRows, 0);
309 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertHyperlinks))
311 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertHyperlinks, 0);
313 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.DeleteColumns))
315 actualLockingValues.Add(Worksheet.SheetProtectionValue.DeleteColumns, 0);
317 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.DeleteRows))
319 actualLockingValues.Add(Worksheet.SheetProtectionValue.DeleteRows, 0);
321 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Sort))
323 actualLockingValues.Add(Worksheet.SheetProtectionValue.Sort, 0);
325 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.AutoFilter))
327 actualLockingValues.Add(Worksheet.SheetProtectionValue.AutoFilter, 0);
329 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.PivotTables))
331 actualLockingValues.Add(Worksheet.SheetProtectionValue.PivotTables, 0);
333 XmlElement sheetProtection = XmlElement.CreateElement(
"sheetProtection");
335 foreach (KeyValuePair<Worksheet.SheetProtectionValue, int> item
in actualLockingValues)
337 temp = Worksheet.GetSheetProtectionName(item.Key);
339 sheetProtection.AddAttribute(temp, ParserUtils.ToString(item.Value));
341 if (passwordWriter.PasswordIsSet())
343 sheetProtection.AddAttributes(passwordWriter.GetAttributes());
345 sheetProtection.AddAttribute(
"sheet",
"1");
346 return sheetProtection;
354 private static List<DynamicRow> GetSortedSheetData(Worksheet worksheet)
356 List<Cell> temp =
new List<Cell>(worksheet.CellValues);
358 DynamicRow row =
new DynamicRow(); ;
359 Dictionary<int, DynamicRow> rows =
new Dictionary<int, DynamicRow>();
363 rowNumber = temp[0].RowNumber;
364 row.RowNumber = rowNumber;
365 foreach (Cell cell
in temp)
367 if (cell.RowNumber != rowNumber)
369 rows.Add(rowNumber, row);
372 RowNumber = cell.RowNumber
374 rowNumber = cell.RowNumber;
376 row.CellDefinitions.Add(cell);
378 if (row.CellDefinitions.Count > 0)
380 rows.Add(rowNumber, row);
383 foreach (KeyValuePair<int, float> rowHeight
in worksheet.RowHeights)
385 if (!rows.ContainsKey(rowHeight.Key))
389 RowNumber = rowHeight.Key
391 rows.Add(rowHeight.Key, row);
394 foreach (KeyValuePair<int, bool> hiddenRow
in worksheet.HiddenRows)
396 if (!rows.ContainsKey(hiddenRow.Key))
400 RowNumber = hiddenRow.Key
402 rows.Add(hiddenRow.Key, row);
405 List<DynamicRow> output = rows.Values.ToList();
406 output.Sort((r1, r2) => r1.RowNumber.CompareTo(r2.RowNumber));
415 private static List<XmlElement> CreatePaneElements(Worksheet worksheet)
417 if (!HasPaneSplitting(worksheet))
421 List<XmlElement> elements =
new List<XmlElement>(2);
422 XmlElement pane = XmlElement.CreateElement(
"pane");
423 bool applyXSplit =
false;
424 bool applyYSplit =
false;
425 if (worksheet.PaneSplitAddress !=
null)
427 bool freeze = worksheet.FreezeSplitPanes !=
null && worksheet.FreezeSplitPanes.Value;
428 int xSplit = worksheet.PaneSplitAddress.Value.Column;
429 int ySplit = worksheet.PaneSplitAddress.Value.Row;
434 pane.AddAttribute(
"xSplit", ParserUtils.ToString(xSplit));
438 pane.AddAttribute(
"xSplit", ParserUtils.ToString(CalculatePaneWidth(worksheet, xSplit)));
446 pane.AddAttribute(
"ySplit", ParserUtils.ToString(ySplit));
450 pane.AddAttribute(
"ySplit", ParserUtils.ToString(CalculatePaneHeight(worksheet, ySplit)));
454 if (freeze && applyXSplit && applyYSplit)
456 pane.AddAttribute(
"state",
"frozenSplit");
460 pane.AddAttribute(
"state",
"frozen");
465 if (worksheet.PaneSplitLeftWidth !=
null)
467 pane.AddAttribute(
"xSplit", ParserUtils.ToString(DataUtils.GetInternalPaneSplitWidth(worksheet.PaneSplitLeftWidth.Value)));
470 if (worksheet.PaneSplitTopHeight !=
null)
472 pane.AddAttribute(
"ySplit", ParserUtils.ToString(DataUtils.GetInternalPaneSplitHeight(worksheet.PaneSplitTopHeight.Value)));
476 if ((applyXSplit || applyYSplit) && worksheet.ActivePane !=
null)
478 switch (worksheet.ActivePane.Value)
480 case Worksheet.WorksheetPane.BottomLeft:
481 pane.AddAttribute(
"activePane",
"bottomLeft");
483 case Worksheet.WorksheetPane.BottomRight:
484 pane.AddAttribute(
"activePane",
"bottomRight");
486 case Worksheet.WorksheetPane.TopLeft:
487 pane.AddAttribute(
"activePane",
"topLeft");
489 case Worksheet.WorksheetPane.TopRight:
490 pane.AddAttribute(
"activePane",
"topRight");
494 string topLeftCell = worksheet.PaneSplitTopLeftCell.Value.GetAddress();
495 pane.AddAttribute(
"topLeftCell", topLeftCell);
497 if (applyXSplit && !applyYSplit)
499 XmlElement selection = XmlElement.CreateElement(
"selection");
500 selection.AddAttribute(
"pane",
"topRight");
501 selection.AddAttribute(
"activeCell", topLeftCell);
502 selection.AddAttribute(
"sqref", topLeftCell);
503 elements.Add(selection);
505 else if (applyYSplit && !applyXSplit)
507 XmlElement selection = XmlElement.CreateElement(
"selection");
508 selection.AddAttribute(
"pane",
"bottomLeft");
509 selection.AddAttribute(
"activeCell", topLeftCell);
510 selection.AddAttribute(
"sqref", topLeftCell);
511 elements.Add(selection);
513 else if (applyYSplit && applyXSplit)
515 XmlElement selection = XmlElement.CreateElement(
"selection");
516 selection.AddAttribute(
"activeCell", topLeftCell);
517 selection.AddAttribute(
"sqref", topLeftCell);
518 elements.Add(selection);
529 private static float CalculatePaneHeight(Worksheet worksheet,
int numberOfRows)
532 for (
int i = 0; i < numberOfRows; i++)
534 if (worksheet.RowHeights.ContainsKey(i))
536 height += DataUtils.GetInternalRowHeight(worksheet.RowHeights[i]);
540 height += DataUtils.GetInternalRowHeight(Worksheet.DefaultWorksheetRowHeight);
543 return DataUtils.GetInternalPaneSplitHeight(height);
552 private static float CalculatePaneWidth(Worksheet worksheet,
int numberOfColumns)
555 for (
int i = 0; i < numberOfColumns; i++)
557 if (worksheet.Columns.ContainsKey(i))
559 width += DataUtils.GetInternalColumnWidth(worksheet.Columns[i].Width);
563 width += DataUtils.GetInternalColumnWidth(Worksheet.DefaultWorksheetColumnWidth);
567 return DataUtils.GetInternalPaneSplitWidth(width) + ((numberOfColumns - 1) * 0f);
575 private static XmlElement CreateColsElement(Worksheet worksheet)
577 XmlElement cols =
null;
578 if (worksheet.Columns.Count == 0)
582 foreach (KeyValuePair<int, Column> column
in worksheet.Columns)
584 if (Comparators.CompareDimensions(column.Value.Width, worksheet.DefaultColumnWidth) == 0 && !column.Value.IsHidden && column.Value.DefaultColumnStyle ==
null)
590 cols = XmlElement.CreateElement(
"cols");
592 XmlElement col = cols.AddChildElement(
"col");
593 col.AddAttribute(
"width", ParserUtils.ToString(DataUtils.GetInternalColumnWidth(column.Value.Width)));
594 string minMax = ParserUtils.ToString(column.Key + 1);
595 col.AddAttribute(
"max", minMax);
596 col.AddAttribute(
"min", minMax);
597 col.AddAttribute(
"customWidth",
"1");
598 if (worksheet.Columns.ContainsKey(column.Key) && worksheet.Columns[column.Key].IsHidden)
600 col.AddAttribute(
"hidden",
"1");
602 if (column.Value.DefaultColumnStyle !=
null)
604 col.AddAttribute(
"style", ParserUtils.ToString(column.Value.DefaultColumnStyle.InternalID.Value));
617 private XmlElement CreateRowElement(DynamicRow dynamicRow, Worksheet worksheet)
619 int rowNumber = dynamicRow.RowNumber;
620 XmlElement row = XmlElement.CreateElementWithAttribute(
"row",
"r", ParserUtils.ToString(rowNumber + 1));
621 if (worksheet.RowHeights.ContainsKey(rowNumber) && Comparators.CompareDimensions(worksheet.RowHeights[rowNumber], worksheet.DefaultRowHeight) != 0)
623 row.AddAttribute(
"dyDescent",
"0.25",
"x14ac");
624 row.AddAttribute(
"customHeight",
"1");
625 row.AddAttribute(
"ht", ParserUtils.ToString(DataUtils.GetInternalRowHeight(worksheet.RowHeights[rowNumber])));
627 if (worksheet.HiddenRows.ContainsKey(rowNumber) && worksheet.HiddenRows[rowNumber])
629 row.AddAttribute(
"hidden",
"1");
632 foreach (Cell item
in dynamicRow.CellDefinitions)
634 string cellValue =
null;
635 XmlAttribute? cellStyle =
null;
636 XmlAttribute? cellType =
null;
637 XmlElement formulaElement =
null;
639 if (item.CellStyle !=
null)
641 cellStyle = XmlAttribute.CreateAttribute(
"s", ParserUtils.ToString(item.CellStyle.InternalID.Value));
644 if (item.DataType == Cell.CellType.Bool)
646 cellType = XmlAttribute.CreateAttribute(
"t",
"b");
647 cellValue = (bool)item.Value ?
"1" :
"0";
650 else if (item.DataType == Cell.CellType.Number)
652 cellType = XmlAttribute.CreateAttribute(
"t",
"n");
653 cellValue = CastNumber(item.Value);
656 else if (item.DataType == Cell.CellType.Date)
658 DateTime date = (DateTime)item.Value;
659 cellValue = DataUtils.GetOADateTimeString(date);
662 else if (item.DataType == Cell.CellType.Time)
664 TimeSpan time = (TimeSpan)item.Value;
665 cellValue = DataUtils.GetOATimeString(time);
667 else if (item.DataType == Cell.CellType.String)
669 cellType = XmlAttribute.CreateAttribute(
"t",
"s");
670 if (item.Value is IFormattableText text)
672 cellValue = sharedStrings.Add(text, ParserUtils.ToString(sharedStrings.Count));
676 cellValue = sharedStrings.Add(
new PlainText(item.Value.ToString()), ParserUtils.ToString(sharedStrings.Count));
678 this.sharedStringWriter.SharedStringsTotalCount++;
680 else if (item.DataType == Cell.CellType.Formula)
682 formulaElement = CreateFormulaElement(item, out cellValue, out cellType);
684 else if (item.DataType == Cell.CellType.Error)
686 cellValue = ResolveErrorValue(item.Value);
687 cellType = XmlAttribute.CreateAttribute(
"t",
"e");
691 XmlElement c = row.AddChildElementWithAttribute(
"c",
"r", item.CellAddress);
692 c.AddAttribute(cellType);
693 c.AddAttribute(cellStyle);
694 if (formulaElement !=
null)
696 c.AddChildElement(formulaElement);
698 if (!
string.IsNullOrEmpty(cellValue))
700 c.AddChildElementWithValue(
"v", XmlUtils.SanitizeXmlValue(cellValue));
712 private static string ResolveErrorValue(
object value)
714 if (value is Errors.FormulaError formulaError)
716 switch (formulaError)
718 case Errors.FormulaError.Null:
719 case Errors.FormulaError.DivisionByZero:
720 case Errors.FormulaError.Value:
721 case Errors.FormulaError.Reference:
722 case Errors.FormulaError.Name:
723 case Errors.FormulaError.Number:
724 case Errors.FormulaError.NotAvailable:
725 case Errors.FormulaError.GettingData:
726 return Errors.FormulaErrorToString(formulaError);
729 else if (value is
string errorText && Errors.TryParseFormulaError(errorText, out Errors.FormulaError parsedError))
731 return Errors.FormulaErrorToString(parsedError);
733 return Errors.FormulaErrorToString(Errors.FormulaError.Name);
743 private static XmlElement CreateFormulaElement(Cell cell, out
string cellValue, out XmlAttribute? cellType)
745 XmlElement formulaElement =
null;
747 XmlAttribute? formulaTypeAttribute =
null;
748 XmlAttribute? formulaRangeAttribute =
null;
749 string formulaExpression;
750 bool omitFormula =
false;
751 switch (cell.Formula.Type)
753 case FormulaData.FormulaType.DataTable:
754 formulaTypeAttribute =
new XmlAttribute(
"t",
"dataTable");
756 case FormulaData.FormulaType.Shared:
757 formulaTypeAttribute =
new XmlAttribute(
"t",
"shared");
759 case FormulaData.FormulaType.Array:
760 formulaTypeAttribute =
new XmlAttribute(
"t",
"array");
763 formulaTypeAttribute =
new XmlAttribute(
"t",
"normal");
766 if (cell.Formula.FormulaRange !=
null)
768 formulaRangeAttribute =
new XmlAttribute(
"ref", cell.Formula.FormulaRange);
770 ResolveFormulaCachedValue(cell.Formula, out cellValue, out cellType);
771 if (cell.Formula.MasterCellAddress !=
null)
775 if (cell.Formula.DefinedNameReference !=
null)
777 if (cell.Formula.DefinedNameReference.Error != Errors.FormulaError.NoError)
779 cellType = XmlAttribute.CreateAttribute(
"t",
"e");
781 formulaExpression = XmlUtils.SanitizeXmlValue(cell.Formula.DefinedNameReference.Name);
785 formulaExpression = XmlUtils.SanitizeXmlValue(cell.Formula.Expression);
789 formulaElement = XmlElement.CreateElement(
"f");
790 formulaElement.AddAttribute(formulaTypeAttribute);
791 formulaElement.AddAttribute(formulaRangeAttribute);
792 formulaElement.InnerValue = formulaExpression;
794 return formulaElement;
803 private static void ResolveFormulaCachedValue(FormulaData formula, out
string cellValue, out XmlAttribute? cellType)
805 cellValue =
string.Empty;
807 if (formula.CachedValue ==
null)
811 Cell.CellType cachedValueType = formula.CachedValueType;
812 if (cachedValueType == Cell.CellType.Default)
814 cachedValueType = FormulaData.ResolveCachedValueType(formula.CachedValue);
816 switch (cachedValueType)
818 case Cell.CellType.Number:
819 cellValue = CastNumber(formula.CachedValue) ?? formula.CachedValue.ToString();
821 case Cell.CellType.Date:
822 if (formula.CachedValue is DateTime dateTime)
824 cellValue = DataUtils.GetOADateTimeString(dateTime);
828 cellValue = formula.CachedValue.ToString();
829 if (!ParserUtils.TryParseDouble(cellValue, out _, NumberStyles.Float))
831 cellType = XmlAttribute.CreateAttribute(
"t",
"d");
835 case Cell.CellType.Time:
836 if (formula.CachedValue is TimeSpan timeSpan)
838 cellValue = DataUtils.GetOATimeString(timeSpan);
842 cellValue = formula.CachedValue.ToString();
845 case Cell.CellType.Bool:
846 if (formula.CachedValue is
bool booleanValue)
848 cellValue = booleanValue ?
"1" :
"0";
852 cellValue = formula.CachedValue.ToString();
853 if (ParserUtils.TryParseBool(cellValue, out booleanValue))
855 cellValue = booleanValue ?
"1" :
"0";
858 cellType = XmlAttribute.CreateAttribute(
"t",
"b");
860 case Cell.CellType.Error:
861 cellValue = formula.CachedValue is Errors.FormulaError formulaError
862 ? Errors.FormulaErrorToString(formulaError)
863 : formula.CachedValue.ToString();
864 cellType = XmlAttribute.CreateAttribute(
"t",
"e");
866 case Cell.CellType.String:
868 cellValue = formula.CachedValue.ToString();
869 cellType = XmlAttribute.CreateAttribute(
"t",
"str");
879 private static string CastNumber(
object item)
881 string cellValue =
null;
882 Type t = item.GetType();
884 if (t == typeof(
byte)) { cellValue = ParserUtils.ToString((
byte)item); }
885 else if (t == typeof(sbyte)) { cellValue = ParserUtils.ToString((sbyte)item); }
886 else if (t == typeof(decimal)) { cellValue = ParserUtils.ToString((decimal)item); }
887 else if (t == typeof(
double)) { cellValue = ParserUtils.ToString((
double)item); }
888 else if (t == typeof(
float)) { cellValue = ParserUtils.ToString((
float)item); }
889 else if (t == typeof(
int)) { cellValue = ParserUtils.ToString((
int)item); }
890 else if (t == typeof(uint)) { cellValue = ParserUtils.ToString((uint)item); }
891 else if (t == typeof(
long)) { cellValue = ParserUtils.ToString((
long)item); }
892 else if (t == typeof(ulong)) { cellValue = ParserUtils.ToString((ulong)item); }
893 else if (t == typeof(
short)) { cellValue = ParserUtils.ToString((
short)item); }
894 else if (t == typeof(ushort)) { cellValue = ParserUtils.ToString((ushort)item); }
906 private List<XmlElement> CreateRowElements(Worksheet worksheet)
908 List<DynamicRow> cellData = GetSortedSheetData(worksheet);
909 List<XmlElement> rows =
new List<XmlElement>(cellData.Count);
910 foreach (DynamicRow row
in cellData)
912 rows.Add(CreateRowElement(row, worksheet));
918 #region helperClasses
922 internal class DynamicRow
924 public int RowNumber {
get;
set; }
929 public List<Cell> CellDefinitions {
get; }
936 this.CellDefinitions =
new List<Cell>();