433 lines
17 KiB
C#
433 lines
17 KiB
C#
using MathOfficeMath = DocumentFormat.OpenXml.Math.OfficeMath;
|
|
|
|
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 MathOfficeMath();
|
|
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 MathOfficeMath();
|
|
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);
|
|
} |