NanoXLSX.Writer 3.0.0-rc.3
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 © 2025
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
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;
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 //TODO add plug-in hook to overwrite password instance
52 this.passwordWriter = new LegacyPasswordWriter(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
136 private static XmlElement CreateMergedCellsElement(Worksheet worksheet)
137 {
138 if (worksheet.MergedCells.Count < 1)
139 {
140 return null;
141 }
142 XmlElement mergeCells = XmlElement.CreateElementWithAttribute("mergeCells", "count", ParserUtils.ToString(worksheet.MergedCells.Count));
143 foreach (KeyValuePair<string, Range> item in worksheet.MergedCells)
144 {
145 mergeCells.AddChildElementWithAttribute("mergeCell", "ref", item.Value.ToString());
146 }
147 return mergeCells;
148 }
149
155 private XmlElement CreateSheetViewElement(Worksheet worksheet)
156 {
157 XmlElement sheetViews = XmlElement.CreateElement("sheetViews");
158 XmlElement sheetView = sheetViews.AddChildElementWithAttribute("sheetView", "workbookViewId", "0");
159 if (Workbook.SelectedWorksheet == worksheet.SheetID - 1 && !worksheet.Hidden)
160 {
161 sheetView.AddAttribute("tabSelected", "1");
162 }
163 if (worksheet.ViewType != Worksheet.SheetViewType.Normal)
164 {
165 if (worksheet.ViewType == Worksheet.SheetViewType.PageLayout)
166 {
167 if (worksheet.ShowRuler)
168 {
169 sheetView.AddAttribute("showRuler", "1");
170 }
171 else
172 {
173 sheetView.AddAttribute("showRuler", "0");
174 }
175 sheetView.AddAttribute("view", "pageLayout");
176 }
177 else if (worksheet.ViewType == Worksheet.SheetViewType.PageBreakPreview)
178 {
179 sheetView.AddAttribute("view", "pageBreakPreview");
180 }
181 }
182 if (!worksheet.ShowGridLines)
183 {
184 sheetView.AddAttribute("showGridLines", "0");
185 }
186 if (!worksheet.ShowRowColumnHeaders)
187 {
188 sheetView.AddAttribute("showRowColHeaders", "0");
189 }
190 sheetView.AddAttribute("zoomScale", ParserUtils.ToString(worksheet.ZoomFactor));
191 foreach (KeyValuePair<Worksheet.SheetViewType, int> scaleFactor in worksheet.ZoomFactors)
192 {
193 if (scaleFactor.Key == worksheet.ViewType)
194 {
195 continue;
196 }
197 if (scaleFactor.Key == Worksheet.SheetViewType.Normal)
198 {
199 sheetView.AddAttribute("zoomScaleNormal", ParserUtils.ToString(scaleFactor.Value));
200 }
201 else if (scaleFactor.Key == Worksheet.SheetViewType.PageBreakPreview)
202 {
203 sheetView.AddAttribute("zoomScaleSheetLayoutView", ParserUtils.ToString(scaleFactor.Value));
204 }
205 else if (scaleFactor.Key == Worksheet.SheetViewType.PageLayout)
206 {
207 sheetView.AddAttribute("zoomScalePageLayoutView", ParserUtils.ToString(scaleFactor.Value));
208 }
209 }
210 sheetView.AddChildElements(CreatePaneElements(worksheet));
211 if (worksheet.SelectedCells.Count > 0)
212 {
213 XmlElement selection = sheetView.AddChildElement("selection");
214 selection.AddAttribute("activeCell", worksheet.SelectedCells[0].StartAddress.ToString());
215 StringBuilder sb = new StringBuilder(worksheet.SelectedCells.Count * 4);
216 for (int i = 0; i < worksheet.SelectedCells.Count; i++)
217 {
218 sb.Append(worksheet.SelectedCells[i].ToString());
219 if (i < worksheet.SelectedCells.Count - 1)
220 {
221 sb.Append(' ');
222 }
223 }
224 selection.AddAttribute("sqref", sb.ToString());
225 }
226 return sheetViews;
227 }
228
234 private static bool HasPaneSplitting(Worksheet worksheet)
235 {
236 if (worksheet.PaneSplitLeftWidth == null && worksheet.PaneSplitTopHeight == null && worksheet.PaneSplitAddress == null)
237 {
238 return false;
239 }
240 return true;
241 }
242
248 private XmlElement CreateSheetProtectionElement(Worksheet worksheet)
249 {
250 if (!worksheet.UseSheetProtection)
251 {
252 return null;
253 }
254 Dictionary<Worksheet.SheetProtectionValue, int> actualLockingValues = new Dictionary<Worksheet.SheetProtectionValue, int>();
255 if (!worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Objects))
256 {
257 actualLockingValues.Add(Worksheet.SheetProtectionValue.Objects, 1);
258 }
259 if (!worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Scenarios))
260 {
261 actualLockingValues.Add(Worksheet.SheetProtectionValue.Scenarios, 1);
262 }
263 bool allowSelectLocked = worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.SelectLockedCells);
264 bool allowSelectUnlocked = worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.SelectUnlockedCells);
265 if (allowSelectLocked && !allowSelectUnlocked)
266 {
267 // This shouldn't happen in Excel's UI, but handle it by allowing both
268 allowSelectUnlocked = true;
269 }
270 if (!allowSelectLocked)
271 {
272 actualLockingValues.Add(Worksheet.SheetProtectionValue.SelectLockedCells, 1);
273 }
274 if (!allowSelectUnlocked)
275 {
276 actualLockingValues.Add(Worksheet.SheetProtectionValue.SelectUnlockedCells, 1);
277 }
278 // Explicit permissions (set to 0 when allowed)
279 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatCells))
280 {
281 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatCells, 0);
282 }
283 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatColumns))
284 {
285 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatColumns, 0);
286 }
287 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.FormatRows))
288 {
289 actualLockingValues.Add(Worksheet.SheetProtectionValue.FormatRows, 0);
290 }
291 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertColumns))
292 {
293 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertColumns, 0);
294 }
295 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertRows))
296 {
297 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertRows, 0);
298 }
299 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.InsertHyperlinks))
300 {
301 actualLockingValues.Add(Worksheet.SheetProtectionValue.InsertHyperlinks, 0);
302 }
303 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.DeleteColumns))
304 {
305 actualLockingValues.Add(Worksheet.SheetProtectionValue.DeleteColumns, 0);
306 }
307 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.DeleteRows))
308 {
309 actualLockingValues.Add(Worksheet.SheetProtectionValue.DeleteRows, 0);
310 }
311 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.Sort))
312 {
313 actualLockingValues.Add(Worksheet.SheetProtectionValue.Sort, 0);
314 }
315 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.AutoFilter))
316 {
317 actualLockingValues.Add(Worksheet.SheetProtectionValue.AutoFilter, 0);
318 }
319 if (worksheet.SheetProtectionValues.Contains(Worksheet.SheetProtectionValue.PivotTables))
320 {
321 actualLockingValues.Add(Worksheet.SheetProtectionValue.PivotTables, 0);
322 }
323 XmlElement sheetProtection = XmlElement.CreateElement("sheetProtection");
324 string temp;
325 foreach (KeyValuePair<Worksheet.SheetProtectionValue, int> item in actualLockingValues)
326 {
327 temp = Worksheet.GetSheetProtectionName(item.Key); // Note! If the enum names differs from the OOXML definitions, this method will cause invalid OOXML entries
328 //temp = Enum.GetName(typeof(Worksheet.SheetProtectionValue), item.Key);
329 sheetProtection.AddAttribute(temp, ParserUtils.ToString(item.Value));
330 }
331 if (passwordWriter.PasswordIsSet())
332 {
333 sheetProtection.AddAttributes(passwordWriter.GetAttributes());
334 }
335 sheetProtection.AddAttribute("sheet", "1");
336 return sheetProtection;
337 }
338
344 private static List<DynamicRow> GetSortedSheetData(Worksheet worksheet)
345 {
346 List<Cell> temp = new List<Cell>();
347 foreach (KeyValuePair<string, Cell> item in worksheet.Cells)
348 {
349 temp.Add(item.Value);
350 }
351 temp.Sort();
352 DynamicRow row = new DynamicRow(); ;
353 Dictionary<int, DynamicRow> rows = new Dictionary<int, DynamicRow>();
354 int rowNumber;
355 if (temp.Count > 0)
356 {
357 rowNumber = temp[0].RowNumber;
358 row.RowNumber = rowNumber;
359 foreach (Cell cell in temp)
360 {
361 if (cell.RowNumber != rowNumber)
362 {
363 rows.Add(rowNumber, row);
364 row = new DynamicRow
365 {
366 RowNumber = cell.RowNumber
367 };
368 rowNumber = cell.RowNumber;
369 }
370 row.CellDefinitions.Add(cell);
371 }
372 if (row.CellDefinitions.Count > 0)
373 {
374 rows.Add(rowNumber, row);
375 }
376 }
377 foreach (KeyValuePair<int, float> rowHeight in worksheet.RowHeights)
378 {
379 if (!rows.ContainsKey(rowHeight.Key))
380 {
381 row = new DynamicRow
382 {
383 RowNumber = rowHeight.Key
384 };
385 rows.Add(rowHeight.Key, row);
386 }
387 }
388 foreach (KeyValuePair<int, bool> hiddenRow in worksheet.HiddenRows)
389 {
390 if (!rows.ContainsKey(hiddenRow.Key))
391 {
392 row = new DynamicRow
393 {
394 RowNumber = hiddenRow.Key
395 };
396 rows.Add(hiddenRow.Key, row);
397 }
398 }
399 List<DynamicRow> output = rows.Values.ToList();
400 output.Sort((r1, r2) => r1.RowNumber.CompareTo(r2.RowNumber)); // Lambda sort
401 return output;
402 }
403
409 private static List<XmlElement> CreatePaneElements(Worksheet worksheet)
410 {
411 if (!HasPaneSplitting(worksheet))
412 {
413 return null;
414 }
415 List<XmlElement> elements = new List<XmlElement>(2);
416 XmlElement pane = XmlElement.CreateElement("pane");
417 bool applyXSplit = false;
418 bool applyYSplit = false;
419 if (worksheet.PaneSplitAddress != null)
420 {
421 bool freeze = worksheet.FreezeSplitPanes != null && worksheet.FreezeSplitPanes.Value;
422 int xSplit = worksheet.PaneSplitAddress.Value.Column;
423 int ySplit = worksheet.PaneSplitAddress.Value.Row;
424 if (xSplit > 0)
425 {
426 if (freeze)
427 {
428 pane.AddAttribute("xSplit", ParserUtils.ToString(xSplit));
429 }
430 else
431 {
432 pane.AddAttribute("xSplit", ParserUtils.ToString(CalculatePaneWidth(worksheet, xSplit)));
433 }
434 applyXSplit = true;
435 }
436 if (ySplit > 0)
437 {
438 if (freeze)
439 {
440 pane.AddAttribute("ySplit", ParserUtils.ToString(ySplit));
441 }
442 else
443 {
444 pane.AddAttribute("ySplit", ParserUtils.ToString(CalculatePaneHeight(worksheet, ySplit)));
445 }
446 applyYSplit = true;
447 }
448 if (freeze && applyXSplit && applyYSplit)
449 {
450 pane.AddAttribute("state", "frozenSplit");
451 }
452 else if (freeze)
453 {
454 pane.AddAttribute("state", "frozen");
455 }
456 }
457 else
458 {
459 if (worksheet.PaneSplitLeftWidth != null)
460 {
461 pane.AddAttribute("xSplit", ParserUtils.ToString(DataUtils.GetInternalPaneSplitWidth(worksheet.PaneSplitLeftWidth.Value)));
462 applyXSplit = true;
463 }
464 if (worksheet.PaneSplitTopHeight != null)
465 {
466 pane.AddAttribute("ySplit", ParserUtils.ToString(DataUtils.GetInternalPaneSplitHeight(worksheet.PaneSplitTopHeight.Value)));
467 applyYSplit = true;
468 }
469 }
470 if ((applyXSplit || applyYSplit) && worksheet.ActivePane != null)
471 {
472 switch (worksheet.ActivePane.Value)
473 {
474 case Worksheet.WorksheetPane.BottomLeft:
475 pane.AddAttribute("activePane", "bottomLeft");
476 break;
477 case Worksheet.WorksheetPane.BottomRight:
478 pane.AddAttribute("activePane", "bottomRight");
479 break;
480 case Worksheet.WorksheetPane.TopLeft:
481 pane.AddAttribute("activePane", "topLeft");
482 break;
483 case Worksheet.WorksheetPane.TopRight:
484 pane.AddAttribute("activePane", "topRight");
485 break;
486 }
487 }
488 string topLeftCell = worksheet.PaneSplitTopLeftCell.Value.GetAddress();
489 pane.AddAttribute("topLeftCell", topLeftCell);
490 elements.Add(pane);
491 if (applyXSplit && !applyYSplit)
492 {
493 XmlElement selection = XmlElement.CreateElement("selection");
494 selection.AddAttribute("pane", "topRight");
495 selection.AddAttribute("activeCell", topLeftCell);
496 selection.AddAttribute("sqref", topLeftCell);
497 elements.Add(selection);
498 }
499 else if (applyYSplit && !applyXSplit)
500 {
501 XmlElement selection = XmlElement.CreateElement("selection");
502 selection.AddAttribute("pane", "bottomLeft");
503 selection.AddAttribute("activeCell", topLeftCell);
504 selection.AddAttribute("sqref", topLeftCell);
505 elements.Add(selection);
506 }
507 else if (applyYSplit && applyXSplit)
508 {
509 XmlElement selection = XmlElement.CreateElement("selection");
510 selection.AddAttribute("activeCell", topLeftCell);
511 selection.AddAttribute("sqref", topLeftCell);
512 elements.Add(selection);
513 }
514 return elements;
515 }
516
523 private static float CalculatePaneHeight(Worksheet worksheet, int numberOfRows)
524 {
525 float height = 0;
526 for (int i = 0; i < numberOfRows; i++)
527 {
528 if (worksheet.RowHeights.ContainsKey(i))
529 {
530 height += DataUtils.GetInternalRowHeight(worksheet.RowHeights[i]);
531 }
532 else
533 {
534 height += DataUtils.GetInternalRowHeight(Worksheet.DefaultWorksheetRowHeight);
535 }
536 }
537 return DataUtils.GetInternalPaneSplitHeight(height);
538 }
539
546 private static float CalculatePaneWidth(Worksheet worksheet, int numberOfColumns)
547 {
548 float width = 0;
549 for (int i = 0; i < numberOfColumns; i++)
550 {
551 if (worksheet.Columns.ContainsKey(i))
552 {
553 width += DataUtils.GetInternalColumnWidth(worksheet.Columns[i].Width);
554 }
555 else
556 {
557 width += DataUtils.GetInternalColumnWidth(Worksheet.DefaultWorksheetColumnWidth);
558 }
559 }
560 // Add padding of 75 per column
561 return DataUtils.GetInternalPaneSplitWidth(width) + ((numberOfColumns - 1) * 0f);
562 }
563
569 private static XmlElement CreateColsElement(Worksheet worksheet)
570 {
571 XmlElement cols = null;
572 if (worksheet.Columns.Count == 0)
573 {
574 return cols;
575 }
576 foreach (KeyValuePair<int, Column> column in worksheet.Columns)
577 {
578 if (Comparators.CompareDimensions(column.Value.Width, worksheet.DefaultColumnWidth) == 0 && !column.Value.IsHidden && column.Value.DefaultColumnStyle == null)
579 {
580 continue;
581 }
582 if (cols == null)
583 {
584 cols = XmlElement.CreateElement("cols");
585 }
586 XmlElement col = cols.AddChildElement("col");
587 col.AddAttribute("width", ParserUtils.ToString(DataUtils.GetInternalColumnWidth(column.Value.Width)));
588 string minMax = ParserUtils.ToString(column.Key + 1); // Add 1 for Address
589 col.AddAttribute("max", minMax);
590 col.AddAttribute("min", minMax);
591 col.AddAttribute("customWidth", "1");
592 if (worksheet.Columns.ContainsKey(column.Key) && worksheet.Columns[column.Key].IsHidden)
593 {
594 col.AddAttribute("hidden", "1");
595 }
596 if (column.Value.DefaultColumnStyle != null)
597 {
598 col.AddAttribute("style", ParserUtils.ToString(column.Value.DefaultColumnStyle.InternalID.Value));
599 }
600 }
601 return cols;
602 }
603
611 private XmlElement CreateRowElement(DynamicRow dynamicRow, Worksheet worksheet)
612 {
613 int rowNumber = dynamicRow.RowNumber;
614 XmlElement row = XmlElement.CreateElementWithAttribute("row", "r", ParserUtils.ToString(rowNumber + 1));
615 if (worksheet.RowHeights.ContainsKey(rowNumber) && Comparators.CompareDimensions(worksheet.RowHeights[rowNumber], worksheet.DefaultRowHeight) != 0)
616 {
617 row.AddAttribute("dyDescent", "0.25", "x14ac");
618 row.AddAttribute("customHeight", "1");
619 row.AddAttribute("ht", ParserUtils.ToString(DataUtils.GetInternalRowHeight(worksheet.RowHeights[rowNumber])));
620 }
621 if (worksheet.HiddenRows.ContainsKey(rowNumber) && worksheet.HiddenRows[rowNumber])
622 {
623 row.AddAttribute("hidden", "1");
624 }
625
626 string valueDef = "";
627 foreach (Cell item in dynamicRow.CellDefinitions)
628 {
629 XmlAttribute? styleDef = null;
630 XmlAttribute? typeDef = null;
631
632 if (item.CellStyle != null)
633 {
634 styleDef = XmlAttribute.CreateAttribute("s", ParserUtils.ToString(item.CellStyle.InternalID.Value));
635 }
636 if (item.DataType == Cell.CellType.Bool)
637 {
638 typeDef = XmlAttribute.CreateAttribute("t", "b");
639 if ((bool)item.Value) { valueDef = "1"; }
640 else { valueDef = "0"; }
641
642 }
643 // Number casting
644 else if (item.DataType == Cell.CellType.Number)
645 {
646 typeDef = XmlAttribute.CreateAttribute("t", "n");
647 Type t = item.Value.GetType();
648
649 if (t == typeof(byte)) { valueDef = ParserUtils.ToString((byte)item.Value); }
650 else if (t == typeof(sbyte)) { valueDef = ParserUtils.ToString((sbyte)item.Value); }
651 else if (t == typeof(decimal)) { valueDef = ParserUtils.ToString((decimal)item.Value); }
652 else if (t == typeof(double)) { valueDef = ParserUtils.ToString((double)item.Value); }
653 else if (t == typeof(float)) { valueDef = ParserUtils.ToString((float)item.Value); }
654 else if (t == typeof(int)) { valueDef = ParserUtils.ToString((int)item.Value); }
655 else if (t == typeof(uint)) { valueDef = ParserUtils.ToString((uint)item.Value); }
656 else if (t == typeof(long)) { valueDef = ParserUtils.ToString((long)item.Value); }
657 else if (t == typeof(ulong)) { valueDef = ParserUtils.ToString((ulong)item.Value); }
658 else if (t == typeof(short)) { valueDef = ParserUtils.ToString((short)item.Value); }
659 else if (t == typeof(ushort)) { valueDef = ParserUtils.ToString((ushort)item.Value); }
660 }
661 // Date parsing
662 else if (item.DataType == Cell.CellType.Date)
663 {
664 DateTime date = (DateTime)item.Value;
665 valueDef = DataUtils.GetOADateTimeString(date);
666 }
667 // Time parsing
668 else if (item.DataType == Cell.CellType.Time)
669 {
670 TimeSpan time = (TimeSpan)item.Value;
671 valueDef = DataUtils.GetOATimeString(time);
672 }
673 else
674 {
675 string typeAttribute = null;
676 if (item.Value == null)
677 {
678 typeAttribute = null;
679 valueDef = null;
680 // No typeDef
681 }
682 else // Handle sharedStrings
683 {
684 if (item.DataType == Cell.CellType.Formula)
685 {
686 typeAttribute = "str";
687 valueDef = item.Value.ToString();
688 }
689 else
690 {
691 typeAttribute = "s";
692 if (item.Value is IFormattableText text)
693 {
694 valueDef = sharedStrings.Add(text, ParserUtils.ToString(sharedStrings.Count));
695 }
696 else
697 {
698 valueDef = sharedStrings.Add(new PlainText(item.Value.ToString()), ParserUtils.ToString(sharedStrings.Count));
699 }
700 this.sharedStringWriter.SharedStringsTotalCount++;
701 }
702 }
703 typeDef = XmlAttribute.CreateAttribute("t", typeAttribute);
704 }
705 if (item.DataType != Cell.CellType.Empty)
706 {
707 XmlElement c = row.AddChildElementWithAttribute("c", "r", item.CellAddress);
708 c.AddAttribute(typeDef);
709 c.AddAttribute(styleDef);
710 if (item.DataType == Cell.CellType.Formula)
711 {
712 c.AddChildElementWithValue("f", XmlUtils.SanitizeXmlValue(item.Value.ToString()));
713 }
714 else
715 {
716 c.AddChildElementWithValue("v", XmlUtils.SanitizeXmlValue(valueDef));
717 }
718 }
719 else if (valueDef == null || item.DataType == Cell.CellType.Empty) // Empty cell
720 {
721 XmlElement c = row.AddChildElementWithAttribute("c", "r", item.CellAddress);
722 c.AddAttribute(styleDef);
723 }
724 }
725 return row;
726 }
727
733 private List<XmlElement> CreateRowElements(Worksheet worksheet)
734 {
735 List<DynamicRow> cellData = GetSortedSheetData(worksheet);
736 List<XmlElement> rows = new List<XmlElement>(cellData.Count);
737 foreach (DynamicRow row in cellData)
738 {
739 rows.Add(CreateRowElement(row, worksheet));
740 }
741 return rows;
742 }
743
744 #endregion
745 #region helperClasses
749 internal class DynamicRow
750 {
751 public int RowNumber { get; set; }
752
756 public List<Cell> CellDefinitions { get; }
757
761 public DynamicRow()
762 {
763 this.CellDefinitions = new List<Cell>();
764 }
765 }
766 #endregion
767
768 }
769}
Static class that contains enums for password handling.