Добавьте файлы проекта.
This commit is contained in:
281
QWERTYkez.ExcelProcessor/Editors/CellBorder.cs
Normal file
281
QWERTYkez.ExcelProcessor/Editors/CellBorder.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
namespace QWERTYkez.ExcelProcessor.Editors;
|
||||
|
||||
/// <summary>
|
||||
/// Определяет границы ячейки: верхнюю, нижнюю, левую, правую и диагональные.
|
||||
/// Каждая граница может иметь стиль и цвет.
|
||||
/// </summary>
|
||||
public readonly struct CellBorder : IEquatable<CellBorder>
|
||||
{
|
||||
/// <summary>Верхняя граница.</summary>
|
||||
public BorderSide? TopBorder { get; init; }
|
||||
|
||||
/// <summary>Нижняя граница.</summary>
|
||||
public BorderSide? BottomBorder { get; init; }
|
||||
|
||||
/// <summary>Левая граница.</summary>
|
||||
public BorderSide? LeftBorder { get; init; }
|
||||
|
||||
/// <summary>Правая граница.</summary>
|
||||
public BorderSide? RightBorder { get; init; }
|
||||
|
||||
/// <summary>Диагональная граница «из левого верхнего в правый нижний» (\\).</summary>
|
||||
public BorderSide? DiagonalLeft { get; init; }
|
||||
|
||||
/// <summary>Диагональная граница «из левого нижнего в правый верхний» (//).</summary>
|
||||
public BorderSide? DiagonalRight { get; init; }
|
||||
|
||||
/// <summary>Создаёт элемент Border для Open XML.</summary>
|
||||
public Border? ToBorder()
|
||||
{
|
||||
bool hasAny = TopBorder.HasValue || BottomBorder.HasValue || LeftBorder.HasValue ||
|
||||
RightBorder.HasValue || DiagonalLeft.HasValue || DiagonalRight.HasValue;
|
||||
if (!hasAny) return null;
|
||||
|
||||
var border = new Border();
|
||||
if (TopBorder.HasValue)
|
||||
border.TopBorder = TopBorder.Value.ToBorderElement<TopBorder>();
|
||||
if (BottomBorder.HasValue)
|
||||
border.BottomBorder = BottomBorder.Value.ToBorderElement<BottomBorder>();
|
||||
if (LeftBorder.HasValue)
|
||||
border.LeftBorder = LeftBorder.Value.ToBorderElement<LeftBorder>();
|
||||
if (RightBorder.HasValue)
|
||||
border.RightBorder = RightBorder.Value.ToBorderElement<RightBorder>();
|
||||
|
||||
// Обработка диагональных границ
|
||||
if (DiagonalLeft.HasValue || DiagonalRight.HasValue)
|
||||
{
|
||||
// Диагональ \\ (из левого верхнего в правый нижний) = DiagonalDown
|
||||
border.DiagonalDown = DiagonalLeft.HasValue;
|
||||
// Диагональ // (из левого нижнего в правый верхний) = DiagonalUp
|
||||
border.DiagonalUp = DiagonalRight.HasValue;
|
||||
|
||||
// Стиль и цвет берём из первой заданной диагонали (например, DiagonalLeft)
|
||||
var diagSide = (DiagonalLeft ?? DiagonalRight)!.Value;
|
||||
border.DiagonalBorder = diagSide.ToBorderElement<DiagonalBorder>();
|
||||
}
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
/// <summary>Создаёт CellBorder из элемента Border Open XML.</summary>
|
||||
public static CellBorder FromBorder(Border? border)
|
||||
{
|
||||
if (border == null)
|
||||
return default;
|
||||
|
||||
var result = new CellBorder
|
||||
{
|
||||
TopBorder = BorderSide.FromBorderProperties(border.TopBorder),
|
||||
BottomBorder = BorderSide.FromBorderProperties(border.BottomBorder),
|
||||
LeftBorder = BorderSide.FromBorderProperties(border.LeftBorder),
|
||||
RightBorder = BorderSide.FromBorderProperties(border.RightBorder)
|
||||
};
|
||||
|
||||
// Диагональные границы
|
||||
if (border.DiagonalDown?.Value == true)
|
||||
result = result with { DiagonalLeft = BorderSide.FromBorderProperties(border.DiagonalBorder) };
|
||||
if (border.DiagonalUp?.Value == true)
|
||||
result = result with { DiagonalRight = BorderSide.FromBorderProperties(border.DiagonalBorder) };
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) => obj is CellBorder other && Equals(other);
|
||||
public bool Equals(CellBorder other) => this == other;
|
||||
|
||||
public static bool operator ==(CellBorder left, CellBorder right) =>
|
||||
Equals(left.TopBorder, right.TopBorder) &&
|
||||
Equals(left.BottomBorder, right.BottomBorder) &&
|
||||
Equals(left.LeftBorder, right.LeftBorder) &&
|
||||
Equals(left.RightBorder, right.RightBorder) &&
|
||||
Equals(left.DiagonalLeft, right.DiagonalLeft) &&
|
||||
Equals(left.DiagonalRight, right.DiagonalRight);
|
||||
|
||||
public static bool operator !=(CellBorder left, CellBorder right) => !(left == right);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 31 + (TopBorder?.GetHashCode() ?? 0);
|
||||
hash = hash * 31 + (BottomBorder?.GetHashCode() ?? 0);
|
||||
hash = hash * 31 + (LeftBorder?.GetHashCode() ?? 0);
|
||||
hash = hash * 31 + (RightBorder?.GetHashCode() ?? 0);
|
||||
hash = hash * 31 + (DiagonalLeft?.GetHashCode() ?? 0);
|
||||
hash = hash * 31 + (DiagonalRight?.GetHashCode() ?? 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Стиль и цвет границы.</summary>
|
||||
public readonly struct BorderSide : IEquatable<BorderSide>
|
||||
{
|
||||
/// <summary>Стиль линии границы.</summary>
|
||||
public BorderStyle? Style { get; init; }
|
||||
|
||||
/// <summary>Цвет границы.</summary>
|
||||
public ExColor? Color { get; init; }
|
||||
|
||||
internal T ToBorderElement<T>() where T : BorderPropertiesType, new()
|
||||
{
|
||||
var element = new T();
|
||||
if (Style.HasValue)
|
||||
{
|
||||
element.Style = Style.Value switch
|
||||
{
|
||||
BorderStyle.Thin => BorderStyleValues.Thin,
|
||||
BorderStyle.Medium => BorderStyleValues.Medium,
|
||||
BorderStyle.Dashed => BorderStyleValues.Dashed,
|
||||
BorderStyle.Dotted => BorderStyleValues.Dotted,
|
||||
BorderStyle.Thick => BorderStyleValues.Thick,
|
||||
BorderStyle.Double => BorderStyleValues.Double,
|
||||
BorderStyle.Hair => BorderStyleValues.Hair,
|
||||
BorderStyle.MediumDashed => BorderStyleValues.MediumDashed,
|
||||
BorderStyle.DashDot => BorderStyleValues.DashDot,
|
||||
BorderStyle.MediumDashDot => BorderStyleValues.MediumDashDot,
|
||||
BorderStyle.DashDotDot => BorderStyleValues.DashDotDot,
|
||||
BorderStyle.MediumDashDotDot => BorderStyleValues.MediumDashDotDot,
|
||||
BorderStyle.SlantDashDot => BorderStyleValues.SlantDashDot,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
}
|
||||
if (Color.HasValue && Color.Value.Color.HasValue)
|
||||
{
|
||||
var c = Color.Value.Color.Value;
|
||||
element.Color = new Color { Rgb = $"{c.R:X2}{c.G:X2}{c.B:X2}" };
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
/// <summary>Создаёт BorderSide из элемента границы Open XML (TopBorder, BottomBorder, LeftBorder, RightBorder, DiagonalBorder).</summary>
|
||||
public static BorderSide FromBorderProperties(BorderPropertiesType? borderElement)
|
||||
{
|
||||
if (borderElement is null)
|
||||
return default;
|
||||
|
||||
var result = new BorderSide();
|
||||
if (borderElement.Style is not null)
|
||||
{
|
||||
result = result with { Style = MapBorderStyleFromExcel(borderElement.Style.Value) };
|
||||
}
|
||||
if (borderElement.Color?.Rgb?.Value is { } rgb && rgb.Length >= 6)
|
||||
{
|
||||
var color = System.Drawing.Color.FromArgb(
|
||||
Convert.ToByte(rgb.Substring(0, 2), 16),
|
||||
Convert.ToByte(rgb.Substring(2, 2), 16),
|
||||
Convert.ToByte(rgb.Substring(4, 2), 16)
|
||||
);
|
||||
result = result with { Color = new ExColor(color) };
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static BorderStyle MapBorderStyleFromExcel(BorderStyleValues value)
|
||||
{
|
||||
if (value == BorderStyleValues.Thin)
|
||||
{
|
||||
return BorderStyle.Thin;
|
||||
}
|
||||
else if (value == BorderStyleValues.Medium)
|
||||
{
|
||||
return BorderStyle.Medium;
|
||||
}
|
||||
else if (value == BorderStyleValues.Dashed)
|
||||
{
|
||||
return BorderStyle.Dashed;
|
||||
}
|
||||
else if (value == BorderStyleValues.Dotted)
|
||||
{
|
||||
return BorderStyle.Dotted;
|
||||
}
|
||||
else if (value == BorderStyleValues.Thick)
|
||||
{
|
||||
return BorderStyle.Thick;
|
||||
}
|
||||
else if (value == BorderStyleValues.Double)
|
||||
{
|
||||
return BorderStyle.Double;
|
||||
}
|
||||
else if (value == BorderStyleValues.Hair)
|
||||
{
|
||||
return BorderStyle.Hair;
|
||||
}
|
||||
else if (value == BorderStyleValues.MediumDashed)
|
||||
{
|
||||
return BorderStyle.MediumDashed;
|
||||
}
|
||||
else if (value == BorderStyleValues.DashDot)
|
||||
{
|
||||
return BorderStyle.DashDot;
|
||||
}
|
||||
else if (value == BorderStyleValues.MediumDashDot)
|
||||
{
|
||||
return BorderStyle.MediumDashDot;
|
||||
}
|
||||
else if (value == BorderStyleValues.DashDotDot)
|
||||
{
|
||||
return BorderStyle.DashDotDot;
|
||||
}
|
||||
else if (value == BorderStyleValues.MediumDashDotDot)
|
||||
{
|
||||
return BorderStyle.MediumDashDotDot;
|
||||
}
|
||||
else if (value == BorderStyleValues.SlantDashDot)
|
||||
{
|
||||
return BorderStyle.SlantDashDot;
|
||||
}
|
||||
else throw new NotSupportedException($"Unsupported border style: {value}");
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj) => obj is BorderSide other && Equals(other);
|
||||
public bool Equals(BorderSide other) => this == other;
|
||||
|
||||
public static bool operator ==(BorderSide left, BorderSide right) =>
|
||||
left.Style == right.Style && Equals(left.Color, right.Color);
|
||||
|
||||
public static bool operator !=(BorderSide left, BorderSide right) => !(left == right);
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 31 + Style.GetHashCode();
|
||||
hash = hash * 31 + (Color?.GetHashCode() ?? 0);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Тип линии границы</summary>
|
||||
public enum BorderStyle
|
||||
{
|
||||
/// <summary> Тонкая линия </summary>
|
||||
Thin,
|
||||
/// <summary> Средняя линия </summary>
|
||||
Medium,
|
||||
/// <summary> Штриховая </summary>
|
||||
Dashed,
|
||||
/// <summary> Точечная </summary>
|
||||
Dotted,
|
||||
/// <summary> Толстая линия </summary>
|
||||
Thick,
|
||||
/// <summary> Двойная линия </summary>
|
||||
Double,
|
||||
/// <summary> Волосяная (очень тонкая) </summary>
|
||||
Hair,
|
||||
/// <summary> Средняя штриховая </summary>
|
||||
MediumDashed,
|
||||
/// <summary> Штрих-пунктирная </summary>
|
||||
DashDot,
|
||||
/// <summary> Средняя штрих-пунктирная </summary>
|
||||
MediumDashDot,
|
||||
/// <summary> Штрих-пунктир-пунктир </summary>
|
||||
DashDotDot,
|
||||
/// <summary> Средняя штрих-пунктир-пунктир </summary>
|
||||
MediumDashDotDot,
|
||||
/// <summary> Наклонная штрих-пунктирная (для диагональных) </summary>
|
||||
SlantDashDot,
|
||||
}
|
||||
Reference in New Issue
Block a user