NanoXLSX.Writer 3.1.0
Loading...
Searching...
No Matches
WorksheetWriter.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 NanoXLSX.Interfaces;
9using NanoXLSX.Interfaces.Writer;
11using NanoXLSX.Registry;
12using NanoXLSX.Registry.Attributes;
13using NanoXLSX.Utils;
14using NanoXLSX.Utils.Xml;
15using System;
16using System.Collections.Generic;
17using System.Linq;
18using System.Text;
19using static NanoXLSX.Enums.Password;
20
22{
26 [NanoXlsxPlugIn(PlugInUUID = PlugInUUID.WorksheetWriter)]
27 internal class WorksheetWriter : IWorksheetWriter
28 {
29 private XmlElement worksheet;
30 private Worksheet currentWorksheet;
31 private IPasswordWriter passwordWriter;
32 private ISortedMap sharedStrings;
33 private ISharedStringWriter sharedStringWriter;
34
35 #region properties
39 public Workbook Workbook { get; set; }
40
44 public Worksheet CurrentWorksheet
45 {
46 get => currentWorksheet;
47 set
48 {
49 currentWorksheet = value;
50 IPassword passwordInstance = ((Worksheet)CurrentWorksheet).SheetProtectionPassword;
51 this.passwordWriter = PlugInLoader.GetPlugIn<IPasswordWriter>(PlugInUUID.PasswordWriter, new LegacyPasswordWriter());
52 this.passwordWriter.Init(PasswordType.WorksheetProtection, passwordInstance.PasswordHash);
53 }
54 }
55
59 public XmlElement XmlElement { get => worksheet; }
60
61 #endregion
62 #region constructors
63
67 internal WorksheetWriter()
68 {
69 }
70
71 #endregion
72 #region methods
77 public void Init(IBaseWriter baseWriter)
78 {
79 this.Workbook = baseWriter.Workbook;
80 this.sharedStringWriter = baseWriter.SharedStringWriter;
81 this.sharedStrings = this.sharedStringWriter.SharedStrings;
82 }
83
87 public void Execute()
88 {
89 Worksheet ws = currentWorksheet;
90 ws.RecalculateAutoFilter();
91 ws.RecalculateColumns();
92 worksheet = XmlElement.CreateElement("worksheet");
93 worksheet.AddDefaultXmlNameSpace("http://schemas.openxmlformats.org/spreadsheetml/2006/main");
94 worksheet.AddNameSpaceAttribute("mc", "xmlns", "http://schemas.openxmlformats.org/markup-compatibility/2006");
95 worksheet.AddNameSpaceAttribute("x14ac", "xmlns", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
96 worksheet.AddAttribute("mc:Ignorable", "x14ac");
97 if (ws.GetLastCellAddress().HasValue && ws.GetFirstCellAddress().HasValue)
98 {
99 worksheet.AddChildElementWithAttribute("dimension", "ref", new Range(ws.GetFirstCellAddress().Value, ws.GetLastCellAddress().Value).ToString());
100 }
101 if (ws.SelectedCells.Count > 0 || ws.PaneSplitTopHeight != null || ws.PaneSplitLeftWidth != null || ws.PaneSplitAddress != null ||
102 ws.Hidden || ws.ZoomFactor != 100 || ws.ZoomFactors.Count > 1 || !ws.ShowGridLines || !ws.ShowRuler || !ws.ShowRowColumnHeaders || ws.ViewType != Worksheet.SheetViewType.Normal)
103 {
104 worksheet.AddChildElement(CreateSheetViewElement(ws));
105 }
106 XmlElement sheetFormatPr = worksheet.AddChildElement("sheetFormatPr");
107 if (!HasPaneSplitting(ws))
108 {
109 // TODO: Find the right calculation to compensate baseColWidth when using pane splitting
110 sheetFormatPr.AddAttribute("defaultColWidth", ParserUtils.ToString(ws.DefaultColumnWidth));
111 }
112 sheetFormatPr.AddAttribute("defaultRowHeight", ParserUtils.ToString(ws.DefaultRowHeight));
113 sheetFormatPr.AddAttribute("baseColWidth", ParserUtils.ToString(ws.DefaultColumnWidth));
114 sheetFormatPr.AddAttribute("dyDescent", "0.25", "x14ac");
115
116 worksheet.AddChildElement(CreateColsElement(ws));
117
118 XmlElement sheetData = worksheet.AddChildElement("sheetData");
119 sheetData.AddChildElements(CreateRowElements(ws));
120
121 worksheet.AddChildElement(CreateMergedCellsElement(ws));
122 worksheet.AddChildElement(CreateSheetProtectionElement(ws));
123 if (ws.AutoFilterRange != null)
124 {
125 worksheet.AddChildElementWithAttribute("autoFilter", "ref", ws.AutoFilterRange.Value.ToString());
126 }
127
128 WriterPlugInHandler.HandleInlineQueuePlugins(ref worksheet, Workbook, PlugInUUID.WorksheetInlineWriter, currentWorksheet.SheetID);
129 }
130
134 void IWorksheetWriter.ReleaseXmlElement()
135 {
136 this.worksheet = null;
137 }
138
144 private static XmlElement CreateMergedCellsElement(Worksheet worksheet)
145 {
146 if (worksheet.MergedCells.Count < 1)
147 {
148 return null;
149 }
150 XmlElement mergeCells = XmlElement.CreateElementWithAttribute("mergeCells", "count", ParserUtils.ToString(worksheet.MergedCells.Count));
151 foreach (KeyValuePair<string, Range> item in worksheet.MergedCells)
152 {
153 mergeCells.AddChildElementWithAttribute("mergeCell", "ref", item.Value.ToString());
154 }
155 return mergeCells;
156 }
157
163 private XmlElement CreateSheetViewElement(Worksheet worksheet)
164 {
165 XmlElement sheetViews = XmlElement.CreateElement("sheetViews");
166 XmlElement sheetView = sheetViews.AddChildElementWithAttribute("sheetView", "workbookViewId", "0");
167 if (Workbook.SelectedWorksheet == worksheet.SheetID - 1 && !worksheet.Hidden)
168 {
169 sheetView.AddAttribute("tabSelected", "1");
170 }
171 if (worksheet.ViewType != Worksheet.SheetViewType.Normal)
172 {
173 if (worksheet.ViewType == Worksheet.SheetViewType.PageLayout)
174 {
175 if (worksheet.ShowRuler)
176 {
177 sheetView.AddAttribute("showRuler", "1");
178 }
179 else
180 {
181 sheetView.AddAttribute("showRuler", "0");
182 }
183 sheetView.AddAttribute("view", "pageLayout");
184 }
185 else if (worksheet.ViewType == Worksheet.SheetViewType.PageBreakPreview)
186 {
187 sheetView.AddAttribute("view", "pageBreakPreview");
188 }
189 }
190 if (!worksheet.ShowGridLines)
191 {
192 sheetView.AddAttribute("showGridLines", "0");
193 }
194 if (!worksheet.ShowRowColumnHeaders)
195 {
196 sheetView.AddAttribute("showRowColHeaders", "0");
197 }
198 sheetView.AddAttribute("zoomScale", ParserUtils.ToString(worksheet.ZoomFactor));
199 foreach (KeyValuePair<Worksheet.SheetViewType, int> scaleFactor in worksheet.ZoomFactors)
200 {
201 if (scaleFactor.Key == worksheet.ViewType)
202 {
203 continue;
204 }
205 if (scaleFactor.Key == Worksheet.SheetViewType.Normal)
206 {
207 sheetView.AddAttribute("zoomScaleNormal", ParserUtils.ToString(scaleFactor.Value));
208 }
209 else if (scaleFactor.Key == Worksheet.SheetViewType.PageBreakPreview)
210 {
211 sheetView.AddAttribute("zoomScaleSheetLayoutView", ParserUtils.ToString(scaleFactor.Value));
212 }
213 else if (scaleFactor.Key == Worksheet.SheetViewType.PageLayout)
214 {
215 sheetView.AddAttribute("zoomScalePageLayoutView", ParserUtils.ToString(scaleFactor.Value));
216 }
217 }
218 sheetView.AddChildElements(CreatePaneElements(worksheet));
219 if (worksheet.SelectedCells.Count > 0)
220 {
221 XmlElement selection = sheetView.AddChildElement("selection");
222 selection.AddAttribute("activeCell", worksheet.SelectedCells[0].StartAddress.ToString());
223 StringBuilder sb = new StringBuilder(worksheet.SelectedCells.Count * 4);
224 for (int i = 0; i < worksheet.SelectedCells.Count; i++)
225 {
226 sb.Append(worksheet.SelectedCells[i].ToString());
227 if (i < worksheet.SelectedCells.Count - 1)
228 {
229 sb.Append(' ');
230 }
231 }
232 selection.AddAttribute("sqref", sb.ToString());
233 }
234 return sheetViews;
235 }
236
242 private static bool HasPaneSplitting(Worksheet worksheet)
243 {
244 if (worksheet.PaneSplitLeftWidth == null && worksheet.PaneSplitTopHeight == null && worksheet.PaneSplitAddress == null)
245 {
246 return false;
247 }
248 return true;
249 }
250
256 private XmlElement CreateSheetProtectionElement(Worksheet worksheet)
257 {
258 if (!worksheet.UseSheetProtection)
259 {
260 return null;
261 }
262 Dictionary<Worksheet.SheetProtectionValue, int> actualLockingValues = new Dictionary<Worksheet.SheetProtectionValue, int>();
263 if (!worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Objects))
264 {
265 actualLockingValues.Add(Worksheet.SheetProtectionValue.Objects, 1);
266 }
267 if (!worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Scenarios))
268 {
269 actualLockingValues.Add(Worksheet.SheetProtectionValue.Scenarios, 1);
270 }
271 bool allowSelectLocked = worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.SelectLockedCells);
272 bool allowSelectUnlocked = worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.SelectUnlockedCells);
273 if (allowSelectLocked && !allowSelectUnlocked)
274 {
275 // This shouldn't happen in Excel's UI, but handle it by allowing both
276 allowSelectUnlocked = true;
277 }
278 if (!allowSelectLocked)
279 {
280 actualLockingValues.Add(Worksheet.SheetProtectionValue.SelectLockedCells, 1);
281 }
282 if (!allowSelectUnlocked)
283 {
284 actualLockingValues.Add(Worksheet.SheetProtectionValue.SelectUnlockedCells, 1);
285 }
286 // Explicit permissions (set to 0 when allowed)
287 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatCells))
288 {
289 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatCells, 0);
290 }
291 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatColumns))
292 {
293 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatColumns, 0);
294 }
295 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatRows))
296 {
297 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatRows, 0);
298 }
299 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertColumns))
300 {
301 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertColumns, 0);
302 }
303 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertRows))
304 {
305 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertRows, 0);
306 }
307 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertHyperlinks))
308 {
309 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertHyperlinks, 0);
310 }
311 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.DeleteColumns))
312 {
313 actualLockingValues.Add(Worksheet.SheetProtectionValue.DeleteColumns, 0);
314 }
315 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.DeleteRows))
316 {
317 actualLockingValues.Add(Worksheet.SheetProtectionValue.DeleteRows, 0);
318 }
319 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Sort))
320 {
321 actualLockingValues.Add(Worksheet.SheetProtectionValue.Sort, 0);
322 }
323 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.AutoFilter))
324 {
325 actualLockingValues.Add(Worksheet.SheetProtectionValue.AutoFilter, 0);
326 }
327 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.PivotTables))
328 {
329 actualLockingValues.Add(Worksheet.SheetProtectionValue.PivotTables, 0);
330 }
331 XmlElement sheetProtection = XmlElement.CreateElement("sheetProtection");
332 string temp;
333 foreach (KeyValuePair<Worksheet.SheetProtectionValue, int> item in actualLockingValues)
334 {
335 temp = Worksheet.GetSheetProtectionName(item.Key); // Note! If the enum names differs from the OOXML definitions, this method will cause invalid OOXML entries
336 //temp = Enum.GetName(typeof(Worksheet.SheetProtectionValue), item.Key);
337 sheetProtection.AddAttribute(temp, ParserUtils.ToString(item.Value));
338 }
339 if (passwordWriter.PasswordIsSet())
340 {
341 sheetProtection.AddAttributes(passwordWriter.GetAttributes());
342 }
343 sheetProtection.AddAttribute("sheet", "1");
344 return sheetProtection;
345 }
346
352 private static List<DynamicRow> GetSortedSheetData(Worksheet worksheet)
353 {
354 List<Cell> temp = new List<Cell>(worksheet.CellValues);
355 temp.Sort();
356 DynamicRow row = new DynamicRow(); ;
357 Dictionary<int, DynamicRow> rows = new Dictionary<int, DynamicRow>();
358 int rowNumber;
359 if (temp.Count > 0)
360 {
361 rowNumber = temp[0].RowNumber;
362 row.RowNumber = rowNumber;
363 foreach (Cell cell in temp)
364 {
365 if (cell.RowNumber != rowNumber)
366 {
367 rows.Add(rowNumber, row);
368 row = new DynamicRow
369 {
370 RowNumber = cell.RowNumber
371 };
372 rowNumber = cell.RowNumber;
373 }
374 row.CellDefinitions.Add(cell);
375 }
376 if (row.CellDefinitions.Count > 0)
377 {
378 rows.Add(rowNumber, row);
379 }
380 }
381 foreach (KeyValuePair<int, float> rowHeight in worksheet.RowHeights)
382 {
383 if (!rows.ContainsKey(rowHeight.Key))
384 {
385 row = new DynamicRow
386 {
387 RowNumber = rowHeight.Key
388 };
389 rows.Add(rowHeight.Key, row);
390 }
391 }
392 foreach (KeyValuePair<int, bool> hiddenRow in worksheet.HiddenRows)
393 {
394 if (!rows.ContainsKey(hiddenRow.Key))
395 {
396 row = new DynamicRow
397 {
398 RowNumber = hiddenRow.Key
399 };
400 rows.Add(hiddenRow.Key, row);
401 }
402 }
403 List<DynamicRow> output = rows.Values.ToList();
404 output.Sort((r1, r2) => r1.RowNumber.CompareTo(r2.RowNumber)); // Lambda sort
405 return output;
406 }
407
413 private static List<XmlElement> CreatePaneElements(Worksheet worksheet)
414 {
415 if (!HasPaneSplitting(worksheet))
416 {
417 return null;
418 }
419 List<XmlElement> elements = new List<XmlElement>(2);
420 XmlElement pane = XmlElement.CreateElement("pane");
421 bool applyXSplit = false;
422 bool applyYSplit = false;
423 if (worksheet.PaneSplitAddress != null)
424 {
425 bool freeze = worksheet.FreezeSplitPanes != null && worksheet.FreezeSplitPanes.Value;
426 int xSplit = worksheet.PaneSplitAddress.Value.Column;
427 int ySplit = worksheet.PaneSplitAddress.Value.Row;
428 if (xSplit > 0)
429 {
430 if (freeze)
431 {
432 pane.AddAttribute("xSplit", ParserUtils.ToString(xSplit));
433 }
434 else
435 {
436 pane.AddAttribute("xSplit", ParserUtils.ToString(CalculatePaneWidth(worksheet, xSplit)));
437 }
438 applyXSplit = true;
439 }
440 if (ySplit > 0)
441 {
442 if (freeze)
443 {
444 pane.AddAttribute("ySplit", ParserUtils.ToString(ySplit));
445 }
446 else
447 {
448 pane.AddAttribute("ySplit", ParserUtils.ToString(CalculatePaneHeight(worksheet, ySplit)));
449 }
450 applyYSplit = true;
451 }
452 if (freeze && applyXSplit && applyYSplit)
453 {
454 pane.AddAttribute("state", "frozenSplit");
455 }
456 else if (freeze)
457 {
458 pane.AddAttribute("state", "frozen");
459 }
460 }
461 else
462 {
463 if (worksheet.PaneSplitLeftWidth != null)
464 {
465 pane.AddAttribute("xSplit", ParserUtils.ToString(DataUtils.GetInternalPaneSplitWidth(worksheet.PaneSplitLeftWidth.Value)));
466 applyXSplit = true;
467 }
468 if (worksheet.PaneSplitTopHeight != null)
469 {
470 pane.AddAttribute("ySplit", ParserUtils.ToString(DataUtils.GetInternalPaneSplitHeight(worksheet.PaneSplitTopHeight.Value)));
471 applyYSplit = true;
472 }
473 }
474 if ((applyXSplit || applyYSplit) && worksheet.ActivePane != null)
475 {
476 switch (worksheet.ActivePane.Value)
477 {
478 case Worksheet.WorksheetPane.BottomLeft:
479 pane.AddAttribute("activePane", "bottomLeft");
480 break;
481 case Worksheet.WorksheetPane.BottomRight:
482 pane.AddAttribute("activePane", "bottomRight");
483 break;
484 case Worksheet.WorksheetPane.TopLeft:
485 pane.AddAttribute("activePane", "topLeft");
486 break;
487 case Worksheet.WorksheetPane.TopRight:
488 pane.AddAttribute("activePane", "topRight");
489 break;
490 }
491 }
492 string topLeftCell = worksheet.PaneSplitTopLeftCell.Value.GetAddress();
493 pane.AddAttribute("topLeftCell", topLeftCell);
494 elements.Add(pane);
495 if (applyXSplit && !applyYSplit)
496 {
497 XmlElement selection = XmlElement.CreateElement("selection");
498 selection.AddAttribute("pane", "topRight");
499 selection.AddAttribute("activeCell", topLeftCell);
500 selection.AddAttribute("sqref", topLeftCell);
501 elements.Add(selection);
502 }
503 else if (applyYSplit && !applyXSplit)
504 {
505 XmlElement selection = XmlElement.CreateElement("selection");
506 selection.AddAttribute("pane", "bottomLeft");
507 selection.AddAttribute("activeCell", topLeftCell);
508 selection.AddAttribute("sqref", topLeftCell);
509 elements.Add(selection);
510 }
511 else if (applyYSplit && applyXSplit)
512 {
513 XmlElement selection = XmlElement.CreateElement("selection");
514 selection.AddAttribute("activeCell", topLeftCell);
515 selection.AddAttribute("sqref", topLeftCell);
516 elements.Add(selection);
517 }
518 return elements;
519 }
520
527 private static float CalculatePaneHeight(Worksheet worksheet, int numberOfRows)
528 {
529 float height = 0;
530 for (int i = 0; i < numberOfRows; i++)
531 {
532 if (worksheet.RowHeights.ContainsKey(i))
533 {
534 height += DataUtils.GetInternalRowHeight(worksheet.RowHeights[i]);
535 }
536 else
537 {
538 height += DataUtils.GetInternalRowHeight(Worksheet.DefaultWorksheetRowHeight);
539 }
540 }
541 return DataUtils.GetInternalPaneSplitHeight(height);
542 }
543
550 private static float CalculatePaneWidth(Worksheet worksheet, int numberOfColumns)
551 {
552 float width = 0;
553 for (int i = 0; i < numberOfColumns; i++)
554 {
555 if (worksheet.Columns.ContainsKey(i))
556 {
557 width += DataUtils.GetInternalColumnWidth(worksheet.Columns[i].Width);
558 }
559 else
560 {
561 width += DataUtils.GetInternalColumnWidth(Worksheet.DefaultWorksheetColumnWidth);
562 }
563 }
564 // Add padding of 75 per column
565 return DataUtils.GetInternalPaneSplitWidth(width) + ((numberOfColumns - 1) * 0f);
566 }
567
573 private static XmlElement CreateColsElement(Worksheet worksheet)
574 {
575 XmlElement cols = null;
576 if (worksheet.Columns.Count == 0)
577 {
578 return cols;
579 }
580 foreach (KeyValuePair<int, Column> column in worksheet.Columns)
581 {
582 if (Comparators.CompareDimensions(column.Value.Width, worksheet.DefaultColumnWidth) == 0 && !column.Value.IsHidden && column.Value.DefaultColumnStyle == null)
583 {
584 continue;
585 }
586 if (cols == null)
587 {
588 cols = XmlElement.CreateElement("cols");
589 }
590 XmlElement col = cols.AddChildElement("col");
591 col.AddAttribute("width", ParserUtils.ToString(DataUtils.GetInternalColumnWidth(column.Value.Width)));
592 string minMax = ParserUtils.ToString(column.Key + 1); // Add 1 for Address
593 col.AddAttribute("max", minMax);
594 col.AddAttribute("min", minMax);
595 col.AddAttribute("customWidth", "1");
596 if (worksheet.Columns.ContainsKey(column.Key) && worksheet.Columns[column.Key].IsHidden)
597 {
598 col.AddAttribute("hidden", "1");
599 }
600 if (column.Value.DefaultColumnStyle != null)
601 {
602 col.AddAttribute("style", ParserUtils.ToString(column.Value.DefaultColumnStyle.InternalID.Value));
603 }
604 }
605 return cols;
606 }
607
615 private XmlElement CreateRowElement(DynamicRow dynamicRow, Worksheet worksheet)
616 {
617 int rowNumber = dynamicRow.RowNumber;
618 XmlElement row = XmlElement.CreateElementWithAttribute("row", "r", ParserUtils.ToString(rowNumber + 1));
619 if (worksheet.RowHeights.ContainsKey(rowNumber) && Comparators.CompareDimensions(worksheet.RowHeights[rowNumber], worksheet.DefaultRowHeight) != 0)
620 {
621 row.AddAttribute("dyDescent", "0.25", "x14ac");
622 row.AddAttribute("customHeight", "1");
623 row.AddAttribute("ht", ParserUtils.ToString(DataUtils.GetInternalRowHeight(worksheet.RowHeights[rowNumber])));
624 }
625 if (worksheet.HiddenRows.ContainsKey(rowNumber) && worksheet.HiddenRows[rowNumber])
626 {
627 row.AddAttribute("hidden", "1");
628 }
629
630 string valueDef = "";
631 foreach (Cell item in dynamicRow.CellDefinitions)
632 {
633 XmlAttribute? styleDef = null;
634 XmlAttribute? typeDef = null;
635
636 if (item.CellStyle != null)
637 {
638 styleDef = XmlAttribute.CreateAttribute("s", ParserUtils.ToString(item.CellStyle.InternalID.Value));
639 }
640 if (item.DataType == Cell.CellType.Bool)
641 {
642 typeDef = XmlAttribute.CreateAttribute("t", "b");
643 if ((bool)item.Value) { valueDef = "1"; }
644 else { valueDef = "0"; }
645
646 }
647 // Number casting
648 else if (item.DataType == Cell.CellType.Number)
649 {
650 typeDef = XmlAttribute.CreateAttribute("t", "n");
651 Type t = item.Value.GetType();
652
653 if (t == typeof(byte)) { valueDef = ParserUtils.ToString((byte)item.Value); }
654 else if (t == typeof(sbyte)) { valueDef = ParserUtils.ToString((sbyte)item.Value); }
655 else if (t == typeof(decimal)) { valueDef = ParserUtils.ToString((decimal)item.Value); }
656 else if (t == typeof(double)) { valueDef = ParserUtils.ToString((double)item.Value); }
657 else if (t == typeof(float)) { valueDef = ParserUtils.ToString((float)item.Value); }
658 else if (t == typeof(int)) { valueDef = ParserUtils.ToString((int)item.Value); }
659 else if (t == typeof(uint)) { valueDef = ParserUtils.ToString((uint)item.Value); }
660 else if (t == typeof(long)) { valueDef = ParserUtils.ToString((long)item.Value); }
661 else if (t == typeof(ulong)) { valueDef = ParserUtils.ToString((ulong)item.Value); }
662 else if (t == typeof(short)) { valueDef = ParserUtils.ToString((short)item.Value); }
663 else if (t == typeof(ushort)) { valueDef = ParserUtils.ToString((ushort)item.Value); }
664 }
665 // Date parsing
666 else if (item.DataType == Cell.CellType.Date)
667 {
668 DateTime date = (DateTime)item.Value;
669 valueDef = DataUtils.GetOADateTimeString(date);
670 }
671 // Time parsing
672 else if (item.DataType == Cell.CellType.Time)
673 {
674 TimeSpan time = (TimeSpan)item.Value;
675 valueDef = DataUtils.GetOATimeString(time);
676 }
677 else
678 {
679 string typeAttribute = null;
680 if (item.Value == null)
681 {
682 typeAttribute = null;
683 valueDef = null;
684 // No typeDef
685 }
686 else // Handle sharedStrings
687 {
688 if (item.DataType == Cell.CellType.Formula)
689 {
690 typeAttribute = "str";
691 valueDef = item.Value.ToString();
692 }
693 else
694 {
695 typeAttribute = "s";
696 if (item.Value is IFormattableText text)
697 {
698 valueDef = sharedStrings.Add(text, ParserUtils.ToString(sharedStrings.Count));
699 }
700 else
701 {
702 valueDef = sharedStrings.Add(new PlainText(item.Value.ToString()), ParserUtils.ToString(sharedStrings.Count));
703 }
704 this.sharedStringWriter.SharedStringsTotalCount++;
705 }
706 }
707 typeDef = XmlAttribute.CreateAttribute("t", typeAttribute);
708 }
709 if (item.DataType != Cell.CellType.Empty)
710 {
711 XmlElement c = row.AddChildElementWithAttribute("c", "r", item.CellAddress);
712 c.AddAttribute(typeDef);
713 c.AddAttribute(styleDef);
714 if (item.DataType == Cell.CellType.Formula)
715 {
716 c.AddChildElementWithValue("f", XmlUtils.SanitizeXmlValue(item.Value.ToString()));
717 }
718 else
719 {
720 c.AddChildElementWithValue("v", XmlUtils.SanitizeXmlValue(valueDef));
721 }
722 }
723 else if (valueDef == null || item.DataType == Cell.CellType.Empty) // Empty cell
724 {
725 XmlElement c = row.AddChildElementWithAttribute("c", "r", item.CellAddress);
726 c.AddAttribute(styleDef);
727 }
728 }
729 return row;
730 }
731
737 private List<XmlElement> CreateRowElements(Worksheet worksheet)
738 {
739 List<DynamicRow> cellData = GetSortedSheetData(worksheet);
740 List<XmlElement> rows = new List<XmlElement>(cellData.Count);
741 foreach (DynamicRow row in cellData)
742 {
743 rows.Add(CreateRowElement(row, worksheet));
744 }
745 return rows;
746 }
747
748 #endregion
749 #region helperClasses
753 internal class DynamicRow
754 {
755 public int RowNumber { get; set; }
756
760 public List<Cell> CellDefinitions { get; }
761
765 public DynamicRow()
766 {
767 this.CellDefinitions = new List<Cell>();
768 }
769 }
770 #endregion
771
772 }
773}