Files
QWERTYkez.OpenXmlProcessors/QWERTYkez.WordProcessor/Builders/ParagraphBuilderBase.cs
melekhin f5eb667973 0.9.1
2026-06-08 14:31:31 +07:00

431 lines
17 KiB
C#

namespace QWERTYkez.WordProcessor;
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);
}