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 _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 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 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 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 _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 denominator) => Division(ToAction(numerator), denominator); public IFormula Division(Action numerator, string denominator) => Division(numerator, ToAction(denominator)); public IFormula Division(Action numerator, Action 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 supText) => Sup(ToAction(baseText), supText); public IFormula Sup(Action baseText, string supText) => Sup(baseText, ToAction(supText)); public IFormula Sup(Action baseText, Action 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 subText) => Sub(ToAction(baseText), subText); public IFormula Sub(Action baseText, string subText) => Sub(baseText, ToAction(subText)); public IFormula Sub(Action baseText, Action 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 supText) => SubSup(ToAction(baseText), ToAction(subText), supText); public IFormula SubSup(string baseText, Action subText, string supText) => SubSup(ToAction(baseText), subText, ToAction(supText)); public IFormula SubSup(string baseText, Action subText, Action supText) => SubSup(ToAction(baseText), subText, supText); public IFormula SubSup(Action baseText, string subText, string supText) => SubSup(baseText, ToAction(subText), ToAction(supText)); public IFormula SubSup(Action baseText, string subText, Action supText) => SubSup(baseText, ToAction(subText), supText); public IFormula SubSup(Action baseText, Action subText, string supText) => SubSup(baseText, subText, ToAction(supText)); public IFormula SubSup(Action baseText, Action subText, Action 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 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 degree) => Root(ToAction(radicand), degree); public IFormula Root(Action radicand, string degree) => Root(radicand, ToAction(degree)); public IFormula Root(Action radicand, Action degree) { var radical = FormulaHelper.CreateRadical(radicand, degree, this); if (TryGetCurrentMathContext(out var element)) element.AppendChild(radical); return this; } private void AddIntegralCore( Action function, Action differential, Action? lowerLimit, Action? 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? upperLimit = null) => Integral(ToAction(function), ToAction(differential), ToNullableAction(lowerLimit), upperLimit); public IFormula Integral(string function, string differential, Action? lowerLimit = null, string? upperLimit = null) => Integral(ToAction(function), ToAction(differential), lowerLimit, ToNullableAction(upperLimit)); public IFormula Integral(string function, string differential, Action? lowerLimit = null, Action? upperLimit = null) => Integral(ToAction(function), ToAction(differential), lowerLimit, upperLimit); public IFormula Integral(string function, Action differential, string? lowerLimit = null, string? upperLimit = null) => Integral(ToAction(function), differential, ToNullableAction(lowerLimit), ToNullableAction(upperLimit)); public IFormula Integral(string function, Action differential, string? lowerLimit = null, Action? upperLimit = null) => Integral(ToAction(function), differential, ToNullableAction(lowerLimit), upperLimit); public IFormula Integral(string function, Action differential, Action? lowerLimit = null, string? upperLimit = null) => Integral(ToAction(function), differential, lowerLimit, ToNullableAction(upperLimit)); public IFormula Integral(string function, Action differential, Action? lowerLimit = null, Action? upperLimit = null) => Integral(ToAction(function), differential, lowerLimit, upperLimit); public IFormula Integral(Action function, string differential, string? lowerLimit = null, string? upperLimit = null) => Integral(function, ToAction(differential), ToNullableAction(lowerLimit), ToNullableAction(upperLimit)); public IFormula Integral(Action function, string differential, string? lowerLimit = null, Action? upperLimit = null) => Integral(function, ToAction(differential), ToNullableAction(lowerLimit), upperLimit); public IFormula Integral(Action function, string differential, Action? lowerLimit = null, string? upperLimit = null) => Integral(function, ToAction(differential), lowerLimit, ToNullableAction(upperLimit)); public IFormula Integral(Action function, string differential, Action? lowerLimit = null, Action? upperLimit = null) => Integral(function, ToAction(differential), lowerLimit, upperLimit); public IFormula Integral(Action function, Action differential, string? lowerLimit = null, string? upperLimit = null) => Integral(function, differential, ToNullableAction(lowerLimit), ToNullableAction(upperLimit)); public IFormula Integral(Action function, Action differential, string? lowerLimit = null, Action? upperLimit = null) => Integral(function, differential, ToNullableAction(lowerLimit), upperLimit); public IFormula Integral(Action function, Action differential, Action? lowerLimit = null, string? upperLimit = null) => Integral(function, differential, lowerLimit, ToNullableAction(upperLimit)); public IFormula Integral(Action function, Action differential, Action? lowerLimit = null, Action? upperLimit = null) { AddIntegralCore(function, differential, lowerLimit, upperLimit); return this; } // Sum private void AddSumCore( Action expression, Action? lowerLimit, Action? 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? upperLimit = null) => Sum(ToAction(expression), ToNullableAction(lowerLimit), upperLimit); public IFormula Sum(string expression, Action? lowerLimit = null, string? upperLimit = null) => Sum(ToAction(expression), lowerLimit, ToNullableAction(upperLimit)); public IFormula Sum(string expression, Action? lowerLimit = null, Action? upperLimit = null) => Sum(ToAction(expression), lowerLimit, upperLimit); public IFormula Sum(Action expression, string? lowerLimit = null, string? upperLimit = null) => Sum(expression, ToNullableAction(lowerLimit), ToNullableAction(upperLimit)); public IFormula Sum(Action expression, string? lowerLimit = null, Action? upperLimit = null) => Sum(expression, ToNullableAction(lowerLimit), upperLimit); public IFormula Sum(Action expression, Action? lowerLimit = null, string? upperLimit = null) => Sum(expression, lowerLimit, ToNullableAction(upperLimit)); public IFormula Sum(Action expression, Action? lowerLimit = null, Action? upperLimit = null) { AddSumCore(expression, lowerLimit, upperLimit); return this; } // Вспомогательный метод для преобразования строки в делегат private static Action? ToNullableAction(string? text) => text is null ? null : (b => b.Text(text)); private static Action ToAction(string text) => b => b.Text(text); }