Добавьте файлы проекта.

This commit is contained in:
melekhin
2026-06-05 15:58:03 +07:00
parent 785bd7dc5d
commit cf8ef7add7
56 changed files with 13478 additions and 0 deletions

View 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,
}