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

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,89 @@
namespace QWERTYkez.WordProcessor.Builders;
public readonly struct CellProps(double? width = null!)
{
public double? Width { get; init; } = width;
public TableVerticalAlignmentValues? VerticalAlignment { get; init; } = default;
public MergedCell? Merge { get; init; } = default;
#pragma warning disable IDE1006 // Стили именования
public static CellProps V_H_ { get; } = new() { Merge = MergedCell.V_H_ };
public static CellProps H_ { get; } = new() { Merge = MergedCell.H_ };
public static CellProps V_ { get; } = new() { Merge = MergedCell.V_ };
public static CellProps _V_H { get; } = new() { Merge = MergedCell._V_H };
public static CellProps _H { get; } = new() { Merge = MergedCell._H };
public static CellProps _V { get; } = new() { Merge = MergedCell._V };
public static CellProps _VH_ { get; } = new() { Merge = MergedCell._VH_ };
public static CellProps V__H { get; } = new() { Merge = MergedCell.V__H };
#pragma warning restore IDE1006 // Стили именования
internal bool TryExtract(out List<OpenXmlElement> list)
{
list =
[
new TableCellVerticalAlignment() { Val = VerticalAlignment ?? TableVerticalAlignmentValues.Center },
new TableCellMargin()
{
TopMargin = new TopMargin() { Width = "0" },
BottomMargin = new BottomMargin() { Width = "0" },
LeftMargin = new LeftMargin() { Width = "0" },
RightMargin = new RightMargin() { Width = "0" }
}
];
if (Width.HasValue)
list.Add(new TableCellWidth()
{
Type = TableWidthUnitValues.Dxa,
Width = ((uint)(Width.Value * 567d)).ToString() // 1 см = 567 DXA
});
if (Merge.HasValue)
{
switch (Merge.Value)
{
case MergedCell.V_H_:
list.Add(new HorizontalMerge() { Val = MergedCellValues.Restart });
list.Add(new VerticalMerge() { Val = MergedCellValues.Restart });
break;
case MergedCell.H_:
list.Add(new HorizontalMerge() { Val = MergedCellValues.Restart }); break;
case MergedCell.V_:
list.Add(new VerticalMerge() { Val = MergedCellValues.Restart }); break;
case MergedCell._V_H:
list.Add(new HorizontalMerge() { Val = MergedCellValues.Continue });
list.Add(new VerticalMerge() { Val = MergedCellValues.Continue });
break;
case MergedCell._H:
list.Add(new HorizontalMerge() { Val = MergedCellValues.Continue }); break;
case MergedCell._V:
list.Add(new VerticalMerge() { Val = MergedCellValues.Continue }); break;
case MergedCell._VH_:
list.Add(new HorizontalMerge() { Val = MergedCellValues.Restart });
list.Add(new VerticalMerge() { Val = MergedCellValues.Continue });
break;
case MergedCell.V__H:
list.Add(new HorizontalMerge() { Val = MergedCellValues.Continue });
list.Add(new VerticalMerge() { Val = MergedCellValues.Restart });
break;
}
}
return list.Count > 0;
}
}
public enum MergedCell
{
V_H_,
H_,
V_,
_V_H,
_H,
_V,
_VH_,
V__H,
}

View File

@@ -0,0 +1,162 @@
using MathStyle = DocumentFormat.OpenXml.Math.Style;
using MathStyleValues = DocumentFormat.OpenXml.Math.StyleValues;
namespace QWERTYkez.WordProcessor.Builders;
public record FontProps
{
public FontProps(double? size = null) => Size = size ?? 12;
public string? FontFamily { get; init; }
public double? Size
{
get => _SizeD.HasValue ? _SizeD.Value / 2d : null;
init => _SizeD = value.HasValue ? (uint?)(value.Value * 2) : null;
}
uint? _SizeD;
public bool IsItalic { get; init; } = false;
public bool IsBold { get; init; } = false;
public UnderlineValues? Underline { get; init; }
public VerticalPositionValues? SubSup { get; init; }
public string? Color { get; init; }
public bool TryExtract(out List<OpenXmlElement> list)
{
list = [];
if (!string.IsNullOrWhiteSpace(FontFamily))
list.Add(new RunFonts { Ascii = FontFamily, HighAnsi = FontFamily });
if (_SizeD is not null)
list.Add(new FontSize { Val = _SizeD.Value.ToString() });
if (IsBold)
list.Add(new Bold());
if (IsItalic)
list.Add(new Italic());
if (Underline is not null)
list.Add(new Underline { Val = Underline });
if (!string.IsNullOrWhiteSpace(Color))
list.Add(new Color { Val = Color });
if (SubSup is not null)
list.Add(new VerticalTextAlignment { Val = SubSup });
return list.Count > 0;
}
public bool TryExtractWithoutFamily(out List<OpenXmlElement> list)
{
list = [];
if (_SizeD is not null)
list.Add(new FontSize { Val = _SizeD.Value.ToString() });
if (IsBold)
list.Add(new Bold());
if (IsItalic)
list.Add(new Italic());
if (Underline is not null)
list.Add(new Underline { Val = Underline });
if (!string.IsNullOrWhiteSpace(Color))
list.Add(new Color { Val = Color });
if (SubSup is not null)
list.Add(new VerticalTextAlignment { Val = SubSup });
return list.Count > 0;
}
public bool TrySupExtract(out List<OpenXmlElement> list)
{
list = [];
if (!string.IsNullOrWhiteSpace(FontFamily))
list.Add(new RunFonts { Ascii = FontFamily, HighAnsi = FontFamily });
if (_SizeD is not null)
list.Add(new FontSize { Val = _SizeD.Value.ToString() });
if (IsBold)
list.Add(new Bold());
if (IsItalic)
list.Add(new Italic());
if (Underline is not null)
list.Add(new Underline { Val = Underline });
if (!string.IsNullOrWhiteSpace(Color))
list.Add(new Color { Val = Color });
list.Add(new VerticalTextAlignment { Val = VerticalPositionValues.Superscript });
return list.Count > 0;
}
public bool TrySubExtract(out List<OpenXmlElement> list)
{
list = [];
if (!string.IsNullOrWhiteSpace(FontFamily))
list.Add(new RunFonts { Ascii = FontFamily, HighAnsi = FontFamily });
if (_SizeD is not null)
list.Add(new FontSize { Val = _SizeD.Value.ToString() });
if (IsBold)
list.Add(new Bold());
if (IsItalic)
list.Add(new Italic());
if (Underline is not null)
list.Add(new Underline { Val = Underline });
if (!string.IsNullOrWhiteSpace(Color))
list.Add(new Color { Val = Color });
list.Add(new VerticalTextAlignment { Val = VerticalPositionValues.Subscript });
return list.Count > 0;
}
public bool TryExtractForMath(out List<OpenXmlElement> list)
{
list = [];
// Для математики используем только элемент Style
if (IsBold)
list.Add(new MathStyle { Val = MathStyleValues.BoldItalic });
else list.Add(new MathStyle { Val = MathStyleValues.Italic });
// Обычное начертание можно не добавлять, так как по умолчанию и так обычное
// (но если нужно явно сбросить, можно добавить Style { Val = StyleValues.Plain })
return list.Count > 0;
}
// Фабричные методы для создания FontProps
public static FontProps Default => new();
public static FontProps WithFont(string fontFamily, double? size = null) =>
new(size) { FontFamily = fontFamily };
public static FontProps WithSize(double size) =>
new(size);
public static FontProps WithStyle(bool bold = false, bool italic = false) =>
new() { IsBold = bold, IsItalic = italic };
public static FontProps WithColor(string hexColor) =>
new() { Color = hexColor };
}

View File

@@ -0,0 +1,357 @@
using Base = DocumentFormat.OpenXml.Math.Base;
using ControlProperties = DocumentFormat.OpenXml.Math.ControlProperties;
using Degree = DocumentFormat.OpenXml.Math.Degree;
using Denominator = DocumentFormat.OpenXml.Math.Denominator;
using Fraction = DocumentFormat.OpenXml.Math.Fraction;
using FractionProperties = DocumentFormat.OpenXml.Math.FractionProperties;
using HideDegree = DocumentFormat.OpenXml.Math.HideDegree;
using MathRun = DocumentFormat.OpenXml.Math.Run;
using MathRunProperties = DocumentFormat.OpenXml.Math.RunProperties;
using MathText = DocumentFormat.OpenXml.Math.Text;
using Numerator = DocumentFormat.OpenXml.Math.Numerator;
using Radical = DocumentFormat.OpenXml.Math.Radical;
using RadicalProperties = DocumentFormat.OpenXml.Math.RadicalProperties;
using SubArgument = DocumentFormat.OpenXml.Math.SubArgument;
using Subscript = DocumentFormat.OpenXml.Math.Subscript;
using SubSuperscript = DocumentFormat.OpenXml.Math.SubSuperscript;
using SuperArgument = DocumentFormat.OpenXml.Math.SuperArgument;
using Superscript = DocumentFormat.OpenXml.Math.Superscript;
namespace QWERTYkez.WordProcessor.Builders;
internal static class FormulaHelper
{
// Вспомогательный метод для создания MathRun с форматированием
public static MathRun CreateMathRun(FontProps? font)
{
var mathRun = new MathRun();
// 1. Математический стиль (полужирный/курсив) добавляем первым
if (font is not null && font.TryExtractForMath(out var mathStyleElements))
{
var mathPr = new MathRunProperties(mathStyleElements);
mathRun.AppendChild(mathPr);
}
// 2. Wordprocessing: цвет, размер, подчёркивание (без семейства шрифта)
var wordPr = new RunProperties();
if (font is not null && font.TryExtractWithoutFamily(out var wordElements))
{
foreach (var elem in wordElements)
wordPr.AppendChild(elem.CloneNode(true));
}
mathRun.AppendChild(wordPr);
return mathRun;
}
public static void AddText(OpenXmlElement parent, string text, FontProps? font)
{
if (parent is MathRun mathRun)
{
mathRun.AppendChild(new MathText(text));
}
else
{
var run = CreateMathRun(font);
run.AppendChild(new MathText(text));
parent.AppendChild(run);
}
}
public static Fraction CreateFraction(
Action<IFormula> numeratorBuilder,
Action<IFormula> denominatorBuilder,
IFormula builder)
{
var fraction = new Fraction();
var font = builder.BaseFont;
// Добавляем свойства дроби с форматированием (для черты дроби)
var fPr = new FractionProperties();
if (font.TryExtractWithoutFamily(out var wordElements))
{
var ctrlPr = new ControlProperties();
var rPr = new RunProperties();
foreach (var elem in wordElements)
rPr.AppendChild(elem.CloneNode(true));
rPr.AppendChild(new RunFonts { Ascii = "Cambria Math", HighAnsi = "Cambria Math" });
ctrlPr.AppendChild(rPr);
fPr.AppendChild(ctrlPr);
}
fraction.AppendChild(fPr);
// Числитель
var numeratorElem = new Numerator();
fraction.AppendChild(numeratorElem);
builder.PushContext(numeratorElem);
numeratorBuilder(builder);
builder.PopContext();
// Знаменатель
var denominatorElem = new Denominator();
fraction.AppendChild(denominatorElem);
builder.PushContext(denominatorElem);
denominatorBuilder(builder);
builder.PopContext();
return fraction;
}
public static Radical CreateRadical(
Action<IFormula> radicandBuilder,
Action<IFormula>? degreeBuilder,
IFormula builder)
{
var radical = new Radical();
var font = builder.BaseFont;
var radPr = new RadicalProperties();
// Цвет, размер и подчёркивание для знака корня (ControlProperties)
if (font is not null && font.TryExtractWithoutFamily(out var wordElements))
{
var ctrlPr = new ControlProperties();
var rPr = new RunProperties();
foreach (var elem in wordElements)
rPr.AppendChild(elem.CloneNode(true));
rPr.AppendChild(new RunFonts { Ascii = "Cambria Math", HighAnsi = "Cambria Math" });
ctrlPr.AppendChild(rPr);
radPr.AppendChild(ctrlPr);
}
// Если степень не задана, скрываем её
if (degreeBuilder is null)
{
radPr.HideDegree = new HideDegree { Val = DocumentFormat.OpenXml.Math.BooleanValues.One };
}
// Добавляем свойства радикала (с цветом)
radical.AppendChild(radPr);
// Степень (если есть)
if (degreeBuilder is not null)
{
var degree = new Degree();
radical.AppendChild(degree);
builder.PushContext(degree);
degreeBuilder(builder);
builder.PopContext();
}
// Подкоренное выражение
var radicand = new Base();
radical.AppendChild(radicand);
builder.PushContext(radicand);
radicandBuilder(builder);
builder.PopContext();
return radical;
}
public static void AddIntegral(
OpenXmlElement currentContext,
Action<IFormula> functionBuilder,
Action<IFormula> differentialBuilder,
Action<IFormula>? lowerLimitBuilder,
Action<IFormula>? upperLimitBuilder,
IFormula builder)
{
var font = builder.BaseFont;
if (lowerLimitBuilder is null && upperLimitBuilder is null)
{
var integralRun = CreateMathRun(font);
integralRun.AppendChild(new MathText("∫"));
currentContext.AppendChild(integralRun);
var funcRun = CreateMathRun(font);
currentContext.AppendChild(funcRun);
builder.PushContext(funcRun);
functionBuilder(builder);
builder.PopContext();
var diffRun = CreateMathRun(font);
currentContext.AppendChild(diffRun);
builder.PushContext(diffRun);
differentialBuilder(builder);
builder.PopContext();
}
else
{
var integralWithLimits = new SubSuperscript();
currentContext.AppendChild(integralWithLimits);
var baseElem = new Base();
var integralRun = CreateMathRun(font);
integralRun.AppendChild(new MathText("∫"));
baseElem.AppendChild(integralRun);
integralWithLimits.AppendChild(baseElem);
if (lowerLimitBuilder is not null)
{
var subArg = new SubArgument();
integralWithLimits.AppendChild(subArg);
builder.PushContext(subArg);
lowerLimitBuilder(builder);
builder.PopContext();
}
if (upperLimitBuilder is not null)
{
var superArg = new SuperArgument();
integralWithLimits.AppendChild(superArg);
builder.PushContext(superArg);
upperLimitBuilder(builder);
builder.PopContext();
}
var funcRun = CreateMathRun(font);
currentContext.AppendChild(funcRun);
builder.PushContext(funcRun);
functionBuilder(builder);
builder.PopContext();
var diffRun = CreateMathRun(font);
currentContext.AppendChild(diffRun);
builder.PushContext(diffRun);
differentialBuilder(builder);
builder.PopContext();
}
}
public static void AddSum(
OpenXmlElement currentContext,
Action<IFormula> expressionBuilder,
Action<IFormula>? lowerLimitBuilder,
Action<IFormula>? upperLimitBuilder,
IFormula builder)
{
var font = builder.BaseFont;
if (lowerLimitBuilder is null && upperLimitBuilder is null)
{
var sumRun = CreateMathRun(font);
sumRun.AppendChild(new MathText("∑"));
currentContext.AppendChild(sumRun);
var exprRun = CreateMathRun(font);
currentContext.AppendChild(exprRun);
builder.PushContext(exprRun);
expressionBuilder(builder);
builder.PopContext();
}
else
{
var sumWithLimits = new SubSuperscript();
currentContext.AppendChild(sumWithLimits);
var baseElem = new Base();
var sumRun = CreateMathRun(font);
sumRun.AppendChild(new MathText("∑"));
baseElem.AppendChild(sumRun);
sumWithLimits.AppendChild(baseElem);
if (lowerLimitBuilder is not null)
{
var subArg = new SubArgument();
sumWithLimits.AppendChild(subArg);
builder.PushContext(subArg);
lowerLimitBuilder(builder);
builder.PopContext();
}
if (upperLimitBuilder is not null)
{
var superArg = new SuperArgument();
sumWithLimits.AppendChild(superArg);
builder.PushContext(superArg);
upperLimitBuilder(builder);
builder.PopContext();
}
var exprRun = CreateMathRun(font);
currentContext.AppendChild(exprRun);
builder.PushContext(exprRun);
expressionBuilder(builder);
builder.PopContext();
}
}
/// <summary>Создаёт степень.</summary>
public static Superscript CreateSuperscript(
Action<IFormula> baseBuilder,
Action<IFormula> supBuilder,
IFormula builder)
{
var superscript = new Superscript();
// Основание
var baseElem = new Base();
superscript.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
// Показатель
var superArg = new SuperArgument();
superscript.AppendChild(superArg);
builder.PushContext(superArg);
supBuilder(builder);
builder.PopContext();
return superscript;
}
/// <summary>Создаёт нижний индекс.</summary>
public static Subscript CreateSubscript(
Action<IFormula> baseBuilder,
Action<IFormula> subBuilder,
IFormula builder)
{
var subscript = new Subscript();
var baseElem = new Base();
subscript.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
var subArg = new SubArgument();
subscript.AppendChild(subArg);
builder.PushContext(subArg);
subBuilder(builder);
builder.PopContext();
return subscript;
}
/// <summary>Создаёт одновременные нижний и верхний индексы.</summary>
public static SubSuperscript CreateSubSuperscript(
Action<IFormula> baseBuilder,
Action<IFormula> subBuilder,
Action<IFormula> supBuilder,
IFormula builder)
{
var subSup = new SubSuperscript();
var baseElem = new Base();
subSup.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
var subArg = new SubArgument();
subSup.AppendChild(subArg);
builder.PushContext(subArg);
subBuilder(builder);
builder.PopContext();
var superArg = new SuperArgument();
subSup.AppendChild(superArg);
builder.PushContext(superArg);
supBuilder(builder);
builder.PopContext();
return subSup;
}
}

View File

@@ -0,0 +1,315 @@
namespace QWERTYkez.WordProcessor.Builders;
public interface ITable
{
/// <summary>Базовый шрифт, используемый по умолчанию для содержимого таблицы.</summary>
FontProps BaseFont { get; }
/// <summary>Добавляет строку в таблицу, настраиваемую с помощью <paramref name="configure"/>.</summary>
/// <param name="configure">Делегат для конфигурации строки через <see cref="IRow"/>.</param>
/// <returns>Тот же экземпляр <see cref="ITable"/> для цепочки вызовов.</returns>
ITable AddRow(Action<IRow> configure);
/// <summary>Добавляет строку с указанной высотой, настраиваемую с помощью <paramref name="configure"/>.</summary>
/// <param name="height">Высота строки в сантиметрах.</param>
/// <param name="configure">Делегат для конфигурации строки через <see cref="IRow"/>.</param>
/// <returns>Тот же экземпляр <see cref="ITable"/> для цепочки вызовов.</returns>
ITable AddRow(double height, Action<IRow> configure);
/// <summary>Добавляет строку с ячейками, содержащими заданные тексты, используя указанный шрифт.</summary>
/// <param name="font">Шрифт для всех ячеек строки.</param>
/// <param name="cellTexts">Массив текстов для ячеек.</param>
/// <returns>Тот же экземпляр <see cref="ITable"/> для цепочки вызовов.</returns>
ITable AddRowWithCells(FontProps font, params string[] cellTexts);
/// <summary>Добавляет строку с ячейками, содержащими заданные тексты, используя базовый шрифт таблицы.</summary>
/// <param name="cellTexts">Массив текстов для ячеек.</param>
/// <returns>Тот же экземпляр <see cref="ITable"/> для цепочки вызовов.</returns>
ITable AddRowWithCells(params string[] cellTexts);
/// <summary>Задаёт свойства таблицы: ширину и стиль границ, выравнивание.</summary>
/// <param name="borderWidth">Ширина границ в пунктах (1/8 pt). По умолчанию 8.</param>
/// <param name="borderValues">Стиль границ. По умолчанию <see cref="BorderValues.Single"/>.</param>
/// <param name="tableAlignment">Выравнивание таблицы на странице. По умолчанию <see cref="TableRowAlignmentValues.Center"/>.</param>
/// <returns>Тот же экземпляр <see cref="ITable"/> для цепочки вызовов.</returns>
ITable Properties(uint borderWidth = 8, BorderValues? borderValues = null, TableRowAlignmentValues? tableAlignment = null);
/// <summary>Создаёт объект <see cref="Table"/> на основе выполненных настроек.</summary>
/// <returns>Готовый объект <see cref="Table"/> для вставки в документ.</returns>
internal Table Build();
}
public interface IRow
{
/// <summary>Базовый шрифт, используемый по умолчанию для содержимого строки.</summary>
FontProps BaseFont { get; }
/// <summary>Добавляет ячейку, настраиваемую с помощью <paramref name="configure"/>.</summary>
/// <param name="configure">Делегат для конфигурации ячейки через <see cref="ICell"/>.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCell(Action<ICell> configure);
/// <summary>Добавляет ячейку с заданными свойствами (без текста).</summary>
/// <param name="cellProps">Свойства ячейки (ширина, объединение, вертикальное выравнивание).</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCell(CellProps cellProps);
/// <summary>Добавляет ячейку с заданными свойствами и текстом (используется базовый шрифт).</summary>
/// <param name="cellProps">Свойства ячейки.</param>
/// <param name="text">Текст ячейки.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCell(CellProps cellProps, string text);
/// <summary>Добавляет ячейку с заданными свойствами, текстом и указанным шрифтом.</summary>
/// <param name="cellProps">Свойства ячейки.</param>
/// <param name="text">Текст ячейки.</param>
/// <param name="font">Шрифт для текста. Если <see langword="null"/>, используется базовый шрифт.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCell(CellProps cellProps, string text, FontProps font);
/// <summary>Добавляет ячейку с простым текстом (используется базовый шрифт).</summary>
/// <param name="text">Текст ячейки.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCell(string text);
/// <summary>Добавляет ячейку с простым текстом и указанным шрифтом.</summary>
/// <param name="text">Текст ячейки.</param>
/// <param name="font">Шрифт для текста. Если <see langword="null"/>, используется базовый шрифт.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCell(string text, FontProps font);
/// <summary>Добавляет ячейку, содержащую один абзац, построенный с помощью <paramref name="configure"/>.</summary>
/// <param name="configure">Делегат для конфигурации абзаца через <see cref="IParagraph"/>.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCellWithPara(Action<IParagraph> configure);
/// <summary>Добавляет ячейку с указанными свойствами, содержащую один абзац, построенный с помощью <paramref name="configure"/>.</summary>
/// <param name="cellProps">Свойства ячейки.</param>
/// <param name="configure">Делегат для конфигурации абзаца через <see cref="IParagraph"/>.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow AddCellWithPara(CellProps cellProps, Action<IParagraph> configure);
/// <summary>Устанавливает точную высоту строки (свойство Exact).</summary>
/// <param name="heightInCm">Высота в сантиметрах.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow SetExactHeight(double heightInCm);
/// <summary>Устанавливает минимальную высоту строки (свойство AtLeast).</summary>
/// <param name="heightInCm">Высота в сантиметрах.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow SetHeight(double heightInCm);
/// <summary>Позволяет напрямую настроить свойства строки <see cref="TableRowProperties"/>.</summary>
/// <param name="configure">Делегат для настройки свойств.</param>
/// <returns>Тот же экземпляр <see cref="IRow"/> для цепочки вызовов.</returns>
IRow SetProperties(Action<TableRowProperties> configure);
/// <summary>Создаёт объект <see cref="TableRow"/> на основе выполненных настроек.</summary>
/// <returns>Готовый объект <see cref="TableRow"/> для вставки в таблицу.</returns>
internal TableRow Build();
}
public interface ICell
{
/// <summary>Базовый шрифт, используемый по умолчанию для содержимого ячейки.</summary>
FontProps BaseFont { get; }
/// <summary>Добавляет в ячейку абзац, содержащий математическую формулу.</summary>
/// <param name="configure">Делегат для конфигурации формулы через <see cref="IFormula"/>.</param>
/// <returns>Тот же экземпляр <see cref="ICell"/> для цепочки вызовов.</returns>
ICell AddFormula(Action<IFormula> configure);
/// <summary>Добавляет в ячейку абзац, построенный с помощью <paramref name="configure"/>.</summary>
/// <param name="configure">Делегат для конфигурации абзаца через <see cref="IParagraph"/>.</param>
/// <returns>Тот же экземпляр <see cref="ICell"/> для цепочки вызовов.</returns>
ICell AddParagraph(Action<IParagraph> configure);
/// <summary>Добавляет в ячейку абзац, состоящий из нескольких строк текста (разделённых переносами).</summary>
/// <param name="lines">Массив строк, каждая строка будет размещена на новой строке абзаца.</param>
/// <returns>Тот же экземпляр <see cref="ICell"/> для цепочки вызовов.</returns>
ICell AddParagraph(params string[] lines);
/// <summary>Добавляет в ячейку абзац с указанным текстом и опциональным шрифтом.</summary>
/// <param name="text">Текст абзаца.</param>
/// <param name="font">Шрифт для текста. Если <see langword="null"/>, используется базовый шрифт.</param>
/// <returns>Тот же экземпляр <see cref="ICell"/> для цепочки вызовов.</returns>
ICell AddParagraph(string text, FontProps? font = null);
/// <summary>Устанавливает свойства ячейки (ширина, объединение, вертикальное выравнивание).</summary>
/// <param name="props">Свойства ячейки.</param>
/// <returns>Тот же экземпляр <see cref="ICell"/> для цепочки вызовов.</returns>
ICell SetCellProps(CellProps props);
/// <summary>Создаёт объект <see cref="TableCell"/> на основе выполненных настроек.</summary>
/// <returns>Готовый объект <see cref="TableCell"/> для вставки в строку.</returns>
internal TableCell Build();
}
public interface IText
{
/// <summary>Базовый шрифт, используемый по умолчанию для содержимого текстового блока.</summary>
FontProps BaseFont { get; }
/// <summary>Добавляет в документ абзац, содержащий математическую формулу.</summary>
/// <param name="configure">Делегат для конфигурации формулы через <see cref="IFormula"/>.</param>
/// <returns>Тот же экземпляр <see cref="IText"/> для цепочки вызовов.</returns>
IText AddFormula(Action<IFormula> configure);
/// <summary>Добавляет абзац, построенный с помощью <paramref name="configure"/>.</summary>
/// <param name="configure">Делегат для конфигурации абзаца через <see cref="IParagraph"/>.</param>
/// <returns>Тот же экземпляр <see cref="IText"/> для цепочки вызовов.</returns>
IText AddParagraph(Action<IParagraph> configure);
/// <summary>Добавляет абзац, состоящий из нескольких строк текста (разделённых переносами).</summary>
/// <param name="lines">Массив строк, каждая строка будет размещена на новой строке абзаца.</param>
/// <returns>Тот же экземпляр <see cref="IText"/> для цепочки вызовов.</returns>
IText AddParagraph(params string[] lines);
/// <summary>Добавляет абзац с указанным текстом и опциональным шрифтом.</summary>
/// <param name="text">Текст абзаца.</param>
/// <param name="font">Шрифт для текста. Если <see langword="null"/>, используется базовый шрифт.</param>
/// <returns>Тот же экземпляр <see cref="IText"/> для цепочки вызовов.</returns>
IText AddParagraph(string text, FontProps? font = null);
/// <summary>Создаёт список абзацев <see cref="Paragraph"/> на основе выполненных настроек.</summary>
/// <returns>Список готовых абзацев для вставки в документ.</returns>
internal List<Paragraph> Build();
}
public interface IParagraph
{
/// <summary>Базовый шрифт, используемый по умолчанию для содержимого абзаца.</summary>
FontProps BaseFont { get; }
/// <summary>Добавляет в абзац математическую формулу (внутри отдельного <see cref="Run"/>).</summary>
/// <param name="configure">Делегат для конфигурации формулы через <see cref="IFormula"/>.</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph AddFormula(Action<IFormula> configure);
/// <summary>Добавляет в абзац математическую формулу (внутри отдельного <see cref="Run"/>) и разрыв строки (<see cref="DocumentFormat.OpenXml.Wordprocessing.Break"/>) в текущий абзац.</summary>
/// <param name="configure">Делегат для конфигурации формулы через <see cref="IFormula"/>.</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph AddFormulaBreak(Action<IFormula> configure);
/// <summary>Добавляет разрыв строки (<see cref="DocumentFormat.OpenXml.Wordprocessing.Break"/>) в текущий абзац.</summary>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph Break();
/// <summary>Добавляет разрыв строки (<see cref="DocumentFormat.OpenXml.Wordprocessing.Break"/>) в текущий абзац.</summary>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph BreakPage();
/// <summary>Добавляет текстовый фрагмент (<see cref="Run"/>) с указанным текстом и опциональным шрифтом.</summary>
/// <param name="text">Текст фрагмента.</param>
/// <param name="font">Шрифт для текста. Если <see langword="null"/>, используется базовый шрифт.</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph AddRun(string text, FontProps? font = null);
/// <summary>Добавляет текстовый фрагмент (<see cref="Run"/>) с указанным текстом и опциональным шрифтом и разрыв строки (<see cref="DocumentFormat.OpenXml.Wordprocessing.Break"/>) в текущий абзац.</summary>
/// <param name="text">Текст фрагмента.</param>
/// <param name="font">Шрифт для текста. Если <see langword="null"/>, используется базовый шрифт.</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph AddRunBreak(string text, FontProps? font = null);
/// <summary>Добавляет текстовый фрагмент с полным контролем над свойствами <see cref="RunProperties"/>.</summary>
/// <param name="text">Текст фрагмента.</param>
/// <param name="configure">Делегат для настройки свойств фрагмента.</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph AddRunWithCustomProps(string text, Action<RunProperties> configure);
/// <summary>Добавляет текстовый фрагмент, отформатированный как нижний индекс (<see cref="VerticalPositionValues.Subscript"/>).</summary>
/// <param name="text">Текст фрагмента.</param>
/// <param name="font">Базовый шрифт (свойства <see cref="SubSup"/> будут переопределены).</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph AddSubRun(string text, FontProps? font = null);
/// <summary>Добавляет текстовый фрагмент, отформатированный как верхний индекс (<see cref="VerticalPositionValues.Superscript"/>).</summary>
/// <param name="text">Текст фрагмента.</param>
/// <param name="font">Базовый шрифт (свойства <see cref="SubSup"/> будут переопределены).</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph AddSupRun(string text, FontProps? font = null);
/// <summary>Устанавливает выравнивание абзаца.</summary>
/// <param name="alignment">Тип выравнивания (лево, право, центр, по ширине).</param>
/// <returns>Тот же экземпляр <see cref="IParagraph"/> для цепочки вызовов.</returns>
IParagraph SetAlignment(JustificationValues alignment);
/// <summary>Создаёт объект <see cref="Paragraph"/> на основе выполненных настроек.</summary>
/// <returns>Готовый объект <see cref="Paragraph"/>.</returns>
internal Paragraph Build();
}
public interface IFormula
{
FontProps BaseFont { get; }
IFormula Text(string text);
IFormula Division(string numerator, string denominator);
IFormula Division(string numerator, Action<IFormula> denominator);
IFormula Division(Action<IFormula> numerator, string denominator);
IFormula Division(Action<IFormula> numerator, Action<IFormula> denominator);
IFormula Sup(string baseText, string supText);
IFormula Sup(string baseText, Action<IFormula> supText);
IFormula Sup(Action<IFormula> baseText, string supText);
IFormula Sup(Action<IFormula> baseText, Action<IFormula> supText);
IFormula Sub(string baseText, string subText);
IFormula Sub(string baseText, Action<IFormula> subText);
IFormula Sub(Action<IFormula> baseText, string subText);
IFormula Sub(Action<IFormula> baseText, Action<IFormula> subText);
IFormula SubSup(string baseText, string subText, string supText);
IFormula SubSup(string baseText, string subText, Action<IFormula> supText);
IFormula SubSup(string baseText, Action<IFormula> subText, string supText);
IFormula SubSup(string baseText, Action<IFormula> subText, Action<IFormula> supText);
IFormula SubSup(Action<IFormula> baseText, string subText, string supText);
IFormula SubSup(Action<IFormula> baseText, string subText, Action<IFormula> supText);
IFormula SubSup(Action<IFormula> baseText, Action<IFormula> subText, string supText);
IFormula SubSup(Action<IFormula> baseText, Action<IFormula> subText, Action<IFormula> supText);
IFormula Root(string radicand);
IFormula Root(Action<IFormula> radicand);
IFormula Root(string radicand, string degree);
IFormula Root(string radicand, Action<IFormula> degree);
IFormula Root(Action<IFormula> radicand, string degree);
IFormula Root(Action<IFormula> radicand, Action<IFormula> degree);
IFormula Integral(string function, string differential, string? lowerLimit = null, string? upperLimit = null);
IFormula Integral(string function, string differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Integral(string function, string differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null);
IFormula Integral(string function, string differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Integral(string function, Action<IFormula> differential, string? lowerLimit = null, string? upperLimit = null);
IFormula Integral(string function, Action<IFormula> differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Integral(string function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null);
IFormula Integral(string function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Integral(Action<IFormula> function, string differential, string? lowerLimit = null, string? upperLimit = null);
IFormula Integral(Action<IFormula> function, string differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Integral(Action<IFormula> function, string differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null);
IFormula Integral(Action<IFormula> function, string differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Integral(Action<IFormula> function, Action<IFormula> differential, string? lowerLimit = null, string? upperLimit = null);
IFormula Integral(Action<IFormula> function, Action<IFormula> differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Integral(Action<IFormula> function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null);
IFormula Integral(Action<IFormula> function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Sum(string expression, string? lowerLimit = null, string? upperLimit = null);
IFormula Sum(string expression, string? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Sum(string expression, Action<IFormula>? lowerLimit = null, string? upperLimit = null);
IFormula Sum(string expression, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Sum(Action<IFormula> expression, string? lowerLimit = null, string? upperLimit = null);
IFormula Sum(Action<IFormula> expression, string? lowerLimit = null, Action<IFormula>? upperLimit = null);
IFormula Sum(Action<IFormula> expression, Action<IFormula>? lowerLimit = null, string? upperLimit = null);
IFormula Sum(Action<IFormula> expression, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null);
internal void PushContext(OpenXmlElement element);
internal void PopContext();
}

View File

@@ -0,0 +1,431 @@
namespace QWERTYkez.WordProcessor.Builders;
abstract class ParagraphBuilderBase : IParagraph, IFormula
{
public FontProps BaseFont { get; internal set; }
// Конструктор для установки базового шрифта
protected ParagraphBuilderBase(FontProps? baseFont = null)
{
BaseFont = baseFont is not null && baseFont.SubSup.HasValue
? baseFont with { SubSup = null }
: baseFont ?? new FontProps();
}
internal FontProps GetEffectiveFont(FontProps? overrideFont) => overrideFont ?? BaseFont;
protected Paragraph _paragraph = null!;
protected List<Run> _runs = null!;
internal IParagraph ParagraphBuilder => CreateParagraphBuilder();
protected virtual IParagraph CreateParagraphBuilder()
{
_paragraph = new Paragraph
{
ParagraphProperties = new ParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines
{
After = "0",
Before = "0",
LineRule = LineSpacingRuleValues.Auto
},
new Indentation
{
Left = "0",
Right = "0",
FirstLine = "0",
Hanging = "0"
}
)
};
_runs = [];
return this;
}
Paragraph IParagraph.Build()
{
foreach (var run in _runs)
{
_paragraph.AppendChild(run);
}
return _paragraph;
}
// Настройка выравнивания
public IParagraph SetAlignment(JustificationValues alignment)
{
var props = _paragraph.ParagraphProperties;
props?.Justification = new Justification { Val = alignment };
return this;
}
// Добавление текста
public IParagraph AddRun(string text, FontProps? font = null)
{
// Заменяем обычные пробелы на неразрывные для сохранения видимости
string processedText = text.Replace(' ', '\u00A0');
var effectiveFont = GetEffectiveFont(font);
Run run;
if (effectiveFont.TryExtract(out var elements))
{
run = new Run(new RunProperties(elements), new Text(processedText));
}
else
{
run = new Run(new Text(processedText));
}
_runs.Add(run);
return this;
}
// Добавление текста
public IParagraph AddRunBreak(string text, FontProps? font = null)
{
// Заменяем обычные пробелы на неразрывные для сохранения видимости
string processedText = text.Replace(' ', '\u00A0');
var effectiveFont = GetEffectiveFont(font);
Run run;
if (effectiveFont.TryExtract(out var elements))
{
run = new Run(new RunProperties(elements), new Text(processedText));
}
else
{
run = new Run(new Text(processedText));
}
_runs.Add(run);
_runs.Add(new Run(new Break()));
return this;
}
// Добавление текста
public IParagraph AddSupRun(string text, FontProps? font = null)
{
var effectiveFont = GetEffectiveFont(font);
Run run;
if (effectiveFont.TrySupExtract(out var elements))
{
run = new Run(new RunProperties(elements), new Text(text));
}
else
{
run = new Run(new Text(text));
}
_runs.Add(run);
return this;
}
// Добавление текста
public IParagraph AddSubRun(string text, FontProps? font = null)
{
var effectiveFont = GetEffectiveFont(font);
Run run;
if (effectiveFont.TrySubExtract(out var elements))
{
run = new Run(new RunProperties(elements), new Text(text));
}
else
{
run = new Run(new Text(text));
}
_runs.Add(run);
return this;
}
public IParagraph AddRunWithCustomProps(string text, Action<RunProperties> configure)
{
var props = new RunProperties();
configure(props);
_runs.Add(new Run(props, new Text(text)));
return this;
}
// Добавление разрыва строки
public IParagraph Break()
{
_runs.Add(new Run(new Break()));
return this;
}
// Добавление разрыва строки
public IParagraph BreakPage()
{
_runs.Add(new Run(new Break() { Type = BreakValues.Page }));
return this;
}
// Метод AddFormula для IParagraphBuilder
IParagraph IParagraph.AddFormula(Action<IFormula> configure)
{
var math = new OfficeMath();
try
{
_mathContextStack.Push(math);
configure(this);
}
finally
{
_mathContextStack.Pop();
}
var run = new Run(); // без RunProperties
run.AppendChild(math);
_runs.Add(run);
return this;
}
// Метод AddFormula для IParagraphBuilder
IParagraph IParagraph.AddFormulaBreak(Action<IFormula> configure)
{
var math = new OfficeMath();
try
{
_mathContextStack.Push(math);
configure(this);
}
finally
{
_mathContextStack.Pop();
}
var run = new Run(); // без RunProperties
run.AppendChild(math);
_runs.Add(run);
_runs.Add(new Run(new Break()));
return this;
}
// Стек для вложенных математических конструкций
private readonly Stack<OpenXmlElement> _mathContextStack = new();
bool TryGetCurrentMathContext(out OpenXmlElement element)
{
if (_mathContextStack.Count > 0)
{
element = _mathContextStack.Peek();
return true;
}
else
{
element = null!;
return false;
}
}
// Вспомогательные методы для стека (используются FormulaHelper)
void IFormula.PushContext(OpenXmlElement element) => _mathContextStack.Push(element);
void IFormula.PopContext() => _mathContextStack.Pop();
// Реализация IFormulaBuilder
public IFormula Text(string text)
{
if (TryGetCurrentMathContext(out var element))
FormulaHelper.AddText(element, text, BaseFont);
return this;
}
// Division
public IFormula Division(string numerator, string denominator) =>
Division(ToAction(numerator), ToAction(denominator));
public IFormula Division(string numerator, Action<IFormula> denominator) =>
Division(ToAction(numerator), denominator);
public IFormula Division(Action<IFormula> numerator, string denominator) =>
Division(numerator, ToAction(denominator));
public IFormula Division(Action<IFormula> numerator, Action<IFormula> denominator)
{
var fraction = FormulaHelper.CreateFraction(numerator, denominator, this);
if (TryGetCurrentMathContext(out var element))
element.AppendChild(fraction);
return this;
}
// Sup
public IFormula Sup(string baseText, string supText) =>
Sup(ToAction(baseText), ToAction(supText));
public IFormula Sup(string baseText, Action<IFormula> supText) =>
Sup(ToAction(baseText), supText);
public IFormula Sup(Action<IFormula> baseText, string supText) =>
Sup(baseText, ToAction(supText));
public IFormula Sup(Action<IFormula> baseText, Action<IFormula> supText)
{
var superscript = FormulaHelper.CreateSuperscript(baseText, supText, this);
if (TryGetCurrentMathContext(out var element))
element.AppendChild(superscript);
return this;
}
// Sub
public IFormula Sub(string baseText, string subText) =>
Sub(ToAction(baseText), ToAction(subText));
public IFormula Sub(string baseText, Action<IFormula> subText) =>
Sub(ToAction(baseText), subText);
public IFormula Sub(Action<IFormula> baseText, string subText) =>
Sub(baseText, ToAction(subText));
public IFormula Sub(Action<IFormula> baseText, Action<IFormula> subText)
{
var subscript = FormulaHelper.CreateSubscript(baseText, subText, this);
if (TryGetCurrentMathContext(out var element))
element.AppendChild(subscript);
return this;
}
// SubSup
public IFormula SubSup(string baseText, string subText, string supText) =>
SubSup(ToAction(baseText), ToAction(subText), ToAction(supText));
public IFormula SubSup(string baseText, string subText, Action<IFormula> supText) =>
SubSup(ToAction(baseText), ToAction(subText), supText);
public IFormula SubSup(string baseText, Action<IFormula> subText, string supText) =>
SubSup(ToAction(baseText), subText, ToAction(supText));
public IFormula SubSup(string baseText, Action<IFormula> subText, Action<IFormula> supText) =>
SubSup(ToAction(baseText), subText, supText);
public IFormula SubSup(Action<IFormula> baseText, string subText, string supText) =>
SubSup(baseText, ToAction(subText), ToAction(supText));
public IFormula SubSup(Action<IFormula> baseText, string subText, Action<IFormula> supText) =>
SubSup(baseText, ToAction(subText), supText);
public IFormula SubSup(Action<IFormula> baseText, Action<IFormula> subText, string supText) =>
SubSup(baseText, subText, ToAction(supText));
public IFormula SubSup(Action<IFormula> baseText, Action<IFormula> subText, Action<IFormula> supText)
{
var subSup = FormulaHelper.CreateSubSuperscript(baseText, subText, supText, this);
if (TryGetCurrentMathContext(out var element))
element.AppendChild(subSup);
return this;
}
// Root
public IFormula Root(string radicand) => Root(ToAction(radicand));
public IFormula Root(Action<IFormula> radicand)
{
var radical = FormulaHelper.CreateRadical(radicand, null, this);
if (TryGetCurrentMathContext(out var element))
element.AppendChild(radical);
return this;
}
public IFormula Root(string radicand, string degree) =>
Root(ToAction(radicand), ToAction(degree));
public IFormula Root(string radicand, Action<IFormula> degree) =>
Root(ToAction(radicand), degree);
public IFormula Root(Action<IFormula> radicand, string degree) =>
Root(radicand, ToAction(degree));
public IFormula Root(Action<IFormula> radicand, Action<IFormula> degree)
{
var radical = FormulaHelper.CreateRadical(radicand, degree, this);
if (TryGetCurrentMathContext(out var element))
element.AppendChild(radical);
return this;
}
private void AddIntegralCore(
Action<IFormula> function,
Action<IFormula> differential,
Action<IFormula>? lowerLimit,
Action<IFormula>? upperLimit)
{
if (TryGetCurrentMathContext(out var element))
FormulaHelper.AddIntegral(element, function, differential, lowerLimit, upperLimit, this);
}
public IFormula Integral(string function, string differential, string? lowerLimit = null, string? upperLimit = null) =>
Integral(ToAction(function), ToAction(differential), ToNullableAction(lowerLimit), ToNullableAction(upperLimit));
public IFormula Integral(string function, string differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Integral(ToAction(function), ToAction(differential), ToNullableAction(lowerLimit), upperLimit);
public IFormula Integral(string function, string differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null) =>
Integral(ToAction(function), ToAction(differential), lowerLimit, ToNullableAction(upperLimit));
public IFormula Integral(string function, string differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Integral(ToAction(function), ToAction(differential), lowerLimit, upperLimit);
public IFormula Integral(string function, Action<IFormula> differential, string? lowerLimit = null, string? upperLimit = null) =>
Integral(ToAction(function), differential, ToNullableAction(lowerLimit), ToNullableAction(upperLimit));
public IFormula Integral(string function, Action<IFormula> differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Integral(ToAction(function), differential, ToNullableAction(lowerLimit), upperLimit);
public IFormula Integral(string function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null) =>
Integral(ToAction(function), differential, lowerLimit, ToNullableAction(upperLimit));
public IFormula Integral(string function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Integral(ToAction(function), differential, lowerLimit, upperLimit);
public IFormula Integral(Action<IFormula> function, string differential, string? lowerLimit = null, string? upperLimit = null) =>
Integral(function, ToAction(differential), ToNullableAction(lowerLimit), ToNullableAction(upperLimit));
public IFormula Integral(Action<IFormula> function, string differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Integral(function, ToAction(differential), ToNullableAction(lowerLimit), upperLimit);
public IFormula Integral(Action<IFormula> function, string differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null) =>
Integral(function, ToAction(differential), lowerLimit, ToNullableAction(upperLimit));
public IFormula Integral(Action<IFormula> function, string differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Integral(function, ToAction(differential), lowerLimit, upperLimit);
public IFormula Integral(Action<IFormula> function, Action<IFormula> differential, string? lowerLimit = null, string? upperLimit = null) =>
Integral(function, differential, ToNullableAction(lowerLimit), ToNullableAction(upperLimit));
public IFormula Integral(Action<IFormula> function, Action<IFormula> differential, string? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Integral(function, differential, ToNullableAction(lowerLimit), upperLimit);
public IFormula Integral(Action<IFormula> function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, string? upperLimit = null) =>
Integral(function, differential, lowerLimit, ToNullableAction(upperLimit));
public IFormula Integral(Action<IFormula> function, Action<IFormula> differential, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null)
{
AddIntegralCore(function, differential, lowerLimit, upperLimit);
return this;
}
// Sum
private void AddSumCore(
Action<IFormula> expression,
Action<IFormula>? lowerLimit,
Action<IFormula>? upperLimit)
{
if (TryGetCurrentMathContext(out var element))
FormulaHelper.AddSum(element, expression, lowerLimit, upperLimit, this);
}
public IFormula Sum(string expression, string? lowerLimit = null, string? upperLimit = null) =>
Sum(ToAction(expression), ToNullableAction(lowerLimit), ToNullableAction(upperLimit));
public IFormula Sum(string expression, string? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Sum(ToAction(expression), ToNullableAction(lowerLimit), upperLimit);
public IFormula Sum(string expression, Action<IFormula>? lowerLimit = null, string? upperLimit = null) =>
Sum(ToAction(expression), lowerLimit, ToNullableAction(upperLimit));
public IFormula Sum(string expression, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Sum(ToAction(expression), lowerLimit, upperLimit);
public IFormula Sum(Action<IFormula> expression, string? lowerLimit = null, string? upperLimit = null) =>
Sum(expression, ToNullableAction(lowerLimit), ToNullableAction(upperLimit));
public IFormula Sum(Action<IFormula> expression, string? lowerLimit = null, Action<IFormula>? upperLimit = null) =>
Sum(expression, ToNullableAction(lowerLimit), upperLimit);
public IFormula Sum(Action<IFormula> expression, Action<IFormula>? lowerLimit = null, string? upperLimit = null) =>
Sum(expression, lowerLimit, ToNullableAction(upperLimit));
public IFormula Sum(Action<IFormula> expression, Action<IFormula>? lowerLimit = null, Action<IFormula>? upperLimit = null)
{
AddSumCore(expression, lowerLimit, upperLimit);
return this;
}
// Вспомогательный метод для преобразования строки в делегат
private static Action<IFormula>? ToNullableAction(string? text) => text is null ? null : (b => b.Text(text));
private static Action<IFormula> ToAction(string text) => b => b.Text(text);
}

View File

@@ -0,0 +1,328 @@
namespace QWERTYkez.WordProcessor.Builders;
internal sealed class TableBuilder : ParagraphBuilderBase, ITable, IRow, ICell
{
private readonly Table _table = new();
internal TableBuilder(FontProps? baseFont) : base(baseFont)
{
}
internal static TableBuilder Create(FontProps? baseFont = null) => new(baseFont);
// Метод завершения сборки
public Table Build() => _table;
// Публичные методы настройки таблицы
public ITable Properties(
uint borderWidth = 8,
BorderValues? borderValues = null,
TableRowAlignmentValues? tableAlignment = null)
{
var borderType = borderValues ?? BorderValues.Single;
var alignment = tableAlignment ?? TableRowAlignmentValues.Center;
_table.AppendChild(new TableProperties(
new TableBorders(
new TopBorder() { Val = borderType, Size = borderWidth },
new BottomBorder() { Val = borderType, Size = borderWidth },
new LeftBorder() { Val = borderType, Size = borderWidth },
new RightBorder() { Val = borderType, Size = borderWidth },
new InsideHorizontalBorder() { Val = borderType, Size = borderWidth },
new InsideVerticalBorder() { Val = borderType, Size = borderWidth }
),
new TableJustification { Val = alignment },
new TableLayout() { Type = TableLayoutValues.Autofit }
));
return this;
}
// Добавление строк с использованием IRowBuilder
public ITable AddRow(Action<IRow> configure)
{
var IRowBuilder = RowBuilder;
configure(this);
_table.AppendChild(IRowBuilder.Build());
return this;
}
public ITable AddRow(double height, Action<IRow> configure)
{
var IRowBuilder = RowBuilder.SetHeight(height);
configure(this);
_table.AppendChild(IRowBuilder.Build());
return this;
}
// Быстрые методы для добавления строк с ячейками
public ITable AddRowWithCells(params string[] cellTexts)
{
AddRow(row =>
{
foreach (var text in cellTexts)
{
row.AddCell(text);
}
});
return this;
}
public ITable AddRowWithCells(FontProps font, params string[] cellTexts)
{
AddRow(row =>
{
foreach (var text in cellTexts)
{
row.AddCell(text, font);
}
});
return this;
}
private TableRow _row = null!;
internal IRow RowBuilder
{
get
{
_row = new();
return this;
}
}
TableRow IRow.Build() => _row;
// Настройка высоты строки (исправленный метод)
public IRow SetHeight(double heightInCm)
{
var height = new TableRowHeight
{
Val = (UInt32Value)(uint)(heightInCm * 567), // 1 см = 567 DXA
HeightType = HeightRuleValues.AtLeast
};
// Убедимся, что TableRowProperties существует
if (_row.TableRowProperties is null)
{
_row.TableRowProperties = new TableRowProperties();
}
else
{
// Удалим существующий TableRowHeight, если он есть
var existingHeight = _row.TableRowProperties.Elements<TableRowHeight>().FirstOrDefault();
existingHeight?.Remove();
}
// Добавляем TableRowHeight как дочерний элемент
_row.TableRowProperties.AppendChild(height);
return this;
}
public IRow SetExactHeight(double heightInCm)
{
var height = new TableRowHeight
{
Val = (UInt32Value)(uint)(heightInCm * 567),
HeightType = HeightRuleValues.Exact
};
// Убедимся, что TableRowProperties существует
if (_row.TableRowProperties is null)
{
_row.TableRowProperties = new TableRowProperties();
}
else
{
// Удалим существующий TableRowHeight, если он есть
var existingHeight = _row.TableRowProperties.Elements<TableRowHeight>().FirstOrDefault();
existingHeight?.Remove();
}
// Добавляем TableRowHeight как дочерний элемент
_row.TableRowProperties.AppendChild(height);
return this;
}
// Добавление ячеек
public IRow AddCell(Action<ICell> configure)
{
var cellBuilder = CellBuilder;
configure(this);
_row.AppendChild(cellBuilder.Build());
return this;
}
public IRow AddCell(string text)
{
var cellBuilder = CellBuilder;
cellBuilder.AddParagraph(p => p.AddRun(text));
_row.AppendChild(cellBuilder.Build());
return this;
}
public IRow AddCell(string text, FontProps font)
{
var cellBuilder = CellBuilder;
if (font is not null)
cellBuilder.AddParagraph(p => p.AddRun(text, font));
else cellBuilder.AddParagraph(p => p.AddRun(text));
_row.AppendChild(cellBuilder.Build());
return this;
}
public IRow AddCell(CellProps cellProps, string text, FontProps font)
{
var cellBuilder = CellBuilder.SetCellProps(cellProps);
if (font is not null)
cellBuilder.AddParagraph(p => p.AddRun(text, font));
else cellBuilder.AddParagraph(p => p.AddRun(text));
_row.AppendChild(cellBuilder.Build());
return this;
}
public IRow AddCell(CellProps cellProps, string text)
{
var cellBuilder = CellBuilder.SetCellProps(cellProps);
cellBuilder.AddParagraph(p => p.AddRun(text));
_row.AppendChild(cellBuilder.Build());
return this;
}
public IRow AddCell(CellProps cellProps)
{
var cellBuilder = CellBuilder.SetCellProps(cellProps);
_row.AppendChild(cellBuilder.Build());
return this;
}
// Метод для установки произвольных свойств строки
public IRow SetProperties(Action<TableRowProperties> configure)
{
_row.TableRowProperties ??= new TableRowProperties();
configure(_row.TableRowProperties);
return this;
}
public IRow AddCellWithPara(Action<IParagraph> configure)
{
var cellBuilder = CellBuilder;
cellBuilder.AddParagraph(configure);
_row.AppendChild(cellBuilder.Build());
return this;
}
public IRow AddCellWithPara(CellProps cellProps, Action<IParagraph> configure)
{
var cellBuilder = CellBuilder.SetCellProps(cellProps);
cellBuilder.AddParagraph(configure);
_row.AppendChild(cellBuilder.Build());
return this;
}
private TableCell _cell = null!;
private List<Paragraph> _paragraphs = null!;
internal ICell CellBuilder
{
get
{
_cell = new();
_paragraphs = [];
return this;
}
}
TableCell ICell.Build()
{
foreach (var paragraph in _paragraphs)
{
_cell.AppendChild(paragraph);
}
return _cell;
}
// Установка свойств ячейки
public ICell SetCellProps(CellProps props)
{
if (props.TryExtract(out var elements))
{
_cell.TableCellProperties = new TableCellProperties(elements);
}
return this;
}
// Добавление параграфов
public ICell AddParagraph(Action<IParagraph> configure)
{
var paraBuilder = ParagraphBuilder;
configure(paraBuilder);
_paragraphs.Add(paraBuilder.Build());
return this;
}
public ICell AddParagraph(string text, FontProps? font = null)
{
var paraBuilder = ParagraphBuilder;
if (font is not null)
{
paraBuilder.AddRun(text, font);
}
else
{
paraBuilder.AddRun(text);
}
_paragraphs.Add(paraBuilder.Build());
return this;
}
public ICell AddParagraph(params string[] lines)
{
var paraBuilder = ParagraphBuilder;
if (lines.Length > 0)
{
paraBuilder.AddRun(lines[0]);
for (int i = 1; i < lines.Length; i++)
{
paraBuilder.Break();
paraBuilder.AddRun(lines[i]);
}
}
_paragraphs.Add(paraBuilder.Build());
return this;
}
// Метод AddFormula для ICellBuilder
ICell ICell.AddFormula(Action<IFormula> configure)
{
var paraBuilder = ParagraphBuilder;
paraBuilder.AddFormula(configure);
_paragraphs.Add(paraBuilder.Build());
return this;
}
}

View File

@@ -0,0 +1,110 @@
namespace QWERTYkez.WordProcessor.Builders;
internal sealed class TextBuilder : ParagraphBuilderBase, IText
{
private readonly ParagraphProperties? _baseParagraphProperties;
internal TextBuilder(FontProps? baseFont, ParagraphProperties? baseParagraphProperties = null) : base(baseFont)
{
_baseParagraphProperties = baseParagraphProperties?.CloneNode(true) as ParagraphProperties;
}
internal static TextBuilder Create(FontProps? baseFont = null, ParagraphProperties? baseParagraphProperties = null) =>
new(baseFont, baseParagraphProperties);
// Переопределяем создание параграфа
protected override IParagraph CreateParagraphBuilder()
{
_paragraph = new Paragraph();
if (_baseParagraphProperties is not null)
{
_paragraph.ParagraphProperties = _baseParagraphProperties.CloneNode(true) as ParagraphProperties;
}
else
{
// Используем стандартные свойства (как в базовом классе)
_paragraph.ParagraphProperties = new ParagraphProperties(
new Justification { Val = JustificationValues.Center },
new SpacingBetweenLines
{
After = "0",
Before = "0",
LineRule = LineSpacingRuleValues.Auto
},
new Indentation
{
Left = "0",
Right = "0",
FirstLine = "0",
Hanging = "0"
}
);
}
_runs = [];
return this;
}
private readonly List<Paragraph> _paragraphs = [];
public List<Paragraph> Build() => _paragraphs;
IText IText.AddFormula(Action<IFormula> configure)
{
var paraBuilder = ParagraphBuilder;
paraBuilder.AddFormula(configure);
_paragraphs.Add(paraBuilder.Build());
return this;
}
// Добавление параграфов
public IText AddParagraph(Action<IParagraph> configure)
{
var paraBuilder = ParagraphBuilder;
configure(paraBuilder);
_paragraphs.Add(paraBuilder.Build());
return this;
}
public IText AddParagraph(string text, FontProps? font = null)
{
var paraBuilder = ParagraphBuilder;
if (font is not null)
{
paraBuilder.AddRun(text, font);
}
else
{
paraBuilder.AddRun(text);
}
_paragraphs.Add(paraBuilder.Build());
return this;
}
public IText AddParagraph(params string[] lines)
{
var paraBuilder = ParagraphBuilder;
if (lines.Length > 0)
{
paraBuilder.AddRun(lines[0]);
for (int i = 1; i < lines.Length; i++)
{
paraBuilder.Break();
paraBuilder.AddRun(lines[i]);
}
}
_paragraphs.Add(paraBuilder.Build());
return this;
}
}