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; 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 numeratorBuilder, Action 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 radicandBuilder, Action? 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 functionBuilder, Action differentialBuilder, Action? lowerLimitBuilder, Action? 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 expressionBuilder, Action? lowerLimitBuilder, Action? 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(); } } /// Создаёт степень. public static Superscript CreateSuperscript( Action baseBuilder, Action 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; } /// Создаёт нижний индекс. public static Subscript CreateSubscript( Action baseBuilder, Action 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; } /// Создаёт одновременные нижний и верхний индексы. public static SubSuperscript CreateSubSuperscript( Action baseBuilder, Action subBuilder, Action 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; } }