26#pragma warning disable CA1805
28#pragma warning restore CA1805
36 public static readonly DateTime
FirstAllowedExcelDate =
new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
40 public static readonly DateTime
LastAllowedExcelDate =
new DateTime(9999, 12, 31, 23, 59, 59, DateTimeKind.Unspecified);
47 public static readonly DateTime
FirstValidExcelDate =
new DateTime(1900, 3, 1, 0, 0, 0, DateTimeKind.Unspecified);
57 private const float COLUMN_WIDTH_ROUNDING_MODIFIER = 256f;
58 private const float SPLIT_WIDTH_MULTIPLIER = 12f;
59 private const float SPLIT_WIDTH_OFFSET = 0.5f;
60 private const float SPLIT_WIDTH_POINT_MULTIPLIER = 3f / 4f;
61 private const float SPLIT_POINT_DIVIDER = 20f;
62 private const float SPLIT_WIDTH_POINT_OFFSET = 390f;
63 private const float SPLIT_HEIGHT_POINT_OFFSET = 300f;
64 private const float ROW_HEIGHT_POINT_MULTIPLIER = 1f / 3f + 1f;
65 private static readonly DateTime ROOT_DATE =
new DateTime(1899, 12, 30, 0, 0, 0, DateTimeKind.Unspecified);
66 private static readonly
double ROOT_MILLIS = (double)
new DateTime(1899, 12, 30, 0, 0, 0, DateTimeKind.Unspecified).Ticks / TimeSpan.TicksPerMillisecond;
130 throw new FormatException(
"The date is not in a valid range for Excel. Dates before 1900-01-01 or after 9999-12-31 are not allowed.");
132 DateTime dateValue = date;
135 dateValue = date.AddDays(-1);
137 double currentMillis = (double)dateValue.Ticks / TimeSpan.TicksPerMillisecond;
138 return ((dateValue.Second + (dateValue.Minute * 60) + (dateValue.Hour * 3600)) / 86400d) + Math.Floor((currentMillis - ROOT_MILLIS) / 86400000d);
161 int seconds = time.Seconds + time.Minutes * 60 + time.Hours * 3600;
162 return time.Days + (double)seconds / 86400d;
181 return ROOT_DATE.AddSeconds(oaDate * 86400d);
207 if (columnWidth <= 0f || maxDigitWidth <= 0f)
211 else if (columnWidth <= 1f)
213 return (
float)Math.Floor((columnWidth * (maxDigitWidth + textPadding)) / maxDigitWidth * COLUMN_WIDTH_ROUNDING_MODIFIER) / COLUMN_WIDTH_ROUNDING_MODIFIER;
217 return (
float)Math.Floor((columnWidth * maxDigitWidth + textPadding) / maxDigitWidth * COLUMN_WIDTH_ROUNDING_MODIFIER) / COLUMN_WIDTH_ROUNDING_MODIFIER;
240 double heightInPixel = Math.Round(rowHeight * ROW_HEIGHT_POINT_MULTIPLIER);
241 return (
float)heightInPixel / ROW_HEIGHT_POINT_MULTIPLIER;
266 pixels = (float)Math.Floor(width / SPLIT_WIDTH_MULTIPLIER + SPLIT_WIDTH_OFFSET);
270 pixels = (float)Math.Floor(width * maxDigitWidth + SPLIT_WIDTH_OFFSET) + textPadding;
272 float points = pixels * SPLIT_WIDTH_POINT_MULTIPLIER;
273 return points * SPLIT_POINT_DIVIDER + SPLIT_WIDTH_POINT_OFFSET;
292 return (
float)Math.Floor(SPLIT_POINT_DIVIDER * height + SPLIT_HEIGHT_POINT_OFFSET);
304 if (internalHeight < 300f)
310 return (internalHeight - SPLIT_HEIGHT_POINT_OFFSET) / SPLIT_POINT_DIVIDER;
323 public static float GetPaneSplitWidth(
float internalWidth,
float maxDigitWidth = 7f,
float textPadding = 5f)
325 float points = (internalWidth - SPLIT_WIDTH_POINT_OFFSET) / SPLIT_POINT_DIVIDER;
332 float width = points / SPLIT_WIDTH_POINT_MULTIPLIER;
333 return (width - textPadding - SPLIT_WIDTH_OFFSET) / maxDigitWidth;
347 List<Range> result =
new List<Range>();
348 List<Range> mergedCandidates =
new List<Range> { newRange };
350 foreach (
Range range
in givenRanges)
352 if (IsMergeCandidate(newRange, range, strategy))
354 mergedCandidates.Add(range);
362 List<Range> slicedRanges = SliceRanges(mergedCandidates);
376 result.AddRange(slicedRanges);
431 List<Range> result =
new List<Range>();
433 foreach (
Range range
in givenRanges)
443 List<Range> subtractedPieces = SubtractRect(range, rangeToRemove);
444 result.AddRange(subtractedPieces);
448 List<Range> slicedRanges = SliceRanges(result);
462 result = slicedRanges;
472 private static List<Range> SliceRanges(List<Range> ranges)
474 HashSet<int> uniqueCols =
new HashSet<int>();
475 HashSet<int> uniqueRows =
new HashSet<int>();
478 foreach (
Range range
in ranges)
487 List<int> sortedCols = uniqueCols.OrderBy(c => c).ToList();
488 List<int> sortedRows = uniqueRows.OrderBy(r => r).ToList();
490 List<Range> slicedRanges =
new List<Range>();
493 for (
int r = 0; r < sortedRows.Count - 1; r++)
495 for (
int c = 0; c < sortedCols.Count - 1; c++)
500 sortedCols[c + 1] - 1,
501 sortedRows[r + 1] - 1
505 if (ranges.Exists(range => range.
Contains(subRange)))
507 slicedRanges.Add(subRange);
519 private static List<Range> SubtractRect(Range original, Range toRemove)
521 List<Range> pieces =
new List<Range>();
523 int orig_left = original.StartAddress.Column;
524 int orig_top = original.StartAddress.Row;
525 int orig_right = original.EndAddress.Column;
526 int orig_bottom = original.EndAddress.Row;
528 int rem_left = toRemove.StartAddress.Column;
529 int rem_top = toRemove.StartAddress.Row;
530 int rem_right = toRemove.EndAddress.Column;
531 int rem_bottom = toRemove.EndAddress.Row;
533 int isct_left = Math.Max(orig_left, rem_left);
534 int isct_top = Math.Max(orig_top, rem_top);
535 int isct_right = Math.Min(orig_right, rem_right);
536 int isct_bottom = Math.Min(orig_bottom, rem_bottom);
540 if (orig_top < isct_top)
542 pieces.Add(
new Range(orig_left, orig_top, orig_right, isct_top - 1));
545 if (isct_bottom < orig_bottom)
547 pieces.Add(
new Range(orig_left, isct_bottom + 1, orig_right, orig_bottom));
550 if (orig_left < isct_left)
552 pieces.Add(
new Range(orig_left, isct_top, isct_left - 1, isct_bottom));
555 if (isct_right < orig_right)
557 pieces.Add(
new Range(isct_right + 1, isct_top, orig_right, isct_bottom));
569 private static List<Range> MergeAdjacentRanges(List<Range> ranges,
RangeMergeStrategy strategy)
571 if (ranges.Count == 0)
573 return new List<Range>();
575 List<Range> mergedRanges =
new List<Range>();
581 var groups = ranges.GroupBy(r =>
new
583 StartCol = r.StartAddress.Column,
584 EndCol = r.EndAddress.Column
586 foreach (var group
in groups)
589 List<Range> sorted = group.OrderBy(r => r.StartAddress.Row).ToList();
590 Range current = sorted[0];
591 for (
int i = 1; i < sorted.Count; i++)
593 Range next = sorted[i];
596 if (current.EndAddress.Row + 1 >= next.StartAddress.Row)
600 int newStartRow = current.StartAddress.Row;
601 int newEndRow = Math.Max(current.EndAddress.Row, next.EndAddress.Row);
603 current.StartAddress.Column, newStartRow,
604 current.EndAddress.Column, newEndRow);
608 mergedRanges.Add(current);
612 mergedRanges.Add(current);
619 var groups = ranges.GroupBy(r =>
new
621 StartRow = r.StartAddress.Row,
622 EndRow = r.EndAddress.Row
624 foreach (var group
in groups)
626 var sorted = group.OrderBy(r => r.StartAddress.Column).ToList();
627 Range current = sorted[0];
628 for (
int i = 1; i < sorted.Count; i++)
630 Range next = sorted[i];
632 if (current.EndAddress.Column + 1 >= next.StartAddress.Column)
634 int newStartCol = current.StartAddress.Column;
635 int newEndCol = Math.Max(current.EndAddress.Column, next.EndAddress.Column);
637 newStartCol, current.StartAddress.Row,
638 newEndCol, current.EndAddress.Row);
642 mergedRanges.Add(current);
646 mergedRanges.Add(current);