Files

357 lines
12 KiB
C#
Raw Permalink Normal View History

2026-06-15 14:37:25 +07:00
using MathBase = DocumentFormat.OpenXml.Math.Base;
using MathControlProperties = DocumentFormat.OpenXml.Math.ControlProperties;
using MathDegree = DocumentFormat.OpenXml.Math.Degree;
using MathDenominator = DocumentFormat.OpenXml.Math.Denominator;
using MathFraction = DocumentFormat.OpenXml.Math.Fraction;
using MathFractionProperties = DocumentFormat.OpenXml.Math.FractionProperties;
using MathHideDegree = DocumentFormat.OpenXml.Math.HideDegree;
using MathRun = DocumentFormat.OpenXml.Math.Run;
using MathRunProperties = DocumentFormat.OpenXml.Math.RunProperties;
using MathText = DocumentFormat.OpenXml.Math.Text;
2026-06-15 14:37:25 +07:00
using MathNumerator = DocumentFormat.OpenXml.Math.Numerator;
using MathRadical = DocumentFormat.OpenXml.Math.Radical;
using MathRadicalProperties = DocumentFormat.OpenXml.Math.RadicalProperties;
using MathSubArgument = DocumentFormat.OpenXml.Math.SubArgument;
using MathSubscript = DocumentFormat.OpenXml.Math.Subscript;
using MathSubSuperscript = DocumentFormat.OpenXml.Math.SubSuperscript;
using MathSuperArgument = DocumentFormat.OpenXml.Math.SuperArgument;
using MathSuperscript = DocumentFormat.OpenXml.Math.Superscript;
2026-06-08 14:31:31 +07:00
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);
}
}
2026-06-15 14:37:25 +07:00
public static MathFraction CreateFraction(
Action<IFormula> numeratorBuilder,
Action<IFormula> denominatorBuilder,
IFormula builder)
{
2026-06-15 14:37:25 +07:00
var fraction = new MathFraction();
var font = builder.BaseFont;
// Добавляем свойства дроби с форматированием (для черты дроби)
2026-06-15 14:37:25 +07:00
var fPr = new MathFractionProperties();
if (font.TryExtractWithoutFamily(out var wordElements))
{
2026-06-15 14:37:25 +07:00
var ctrlPr = new MathControlProperties();
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);
// Числитель
2026-06-15 14:37:25 +07:00
var numeratorElem = new MathNumerator();
fraction.AppendChild(numeratorElem);
builder.PushContext(numeratorElem);
numeratorBuilder(builder);
builder.PopContext();
// Знаменатель
2026-06-15 14:37:25 +07:00
var denominatorElem = new MathDenominator();
fraction.AppendChild(denominatorElem);
builder.PushContext(denominatorElem);
denominatorBuilder(builder);
builder.PopContext();
return fraction;
}
2026-06-15 14:37:25 +07:00
public static MathRadical CreateRadical(
Action<IFormula> radicandBuilder,
Action<IFormula>? degreeBuilder,
IFormula builder)
{
2026-06-15 14:37:25 +07:00
var radical = new MathRadical();
var font = builder.BaseFont;
2026-06-15 14:37:25 +07:00
var radPr = new MathRadicalProperties();
// Цвет, размер и подчёркивание для знака корня (ControlProperties)
if (font is not null && font.TryExtractWithoutFamily(out var wordElements))
{
2026-06-15 14:37:25 +07:00
var ctrlPr = new MathControlProperties();
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)
{
2026-06-15 14:37:25 +07:00
radPr.HideDegree = new MathHideDegree { Val = DocumentFormat.OpenXml.Math.BooleanValues.One };
}
// Добавляем свойства радикала (с цветом)
radical.AppendChild(radPr);
// Степень (если есть)
if (degreeBuilder is not null)
{
2026-06-15 14:37:25 +07:00
var degree = new MathDegree();
radical.AppendChild(degree);
builder.PushContext(degree);
degreeBuilder(builder);
builder.PopContext();
}
// Подкоренное выражение
2026-06-15 14:37:25 +07:00
var radicand = new MathBase();
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
{
2026-06-15 14:37:25 +07:00
var integralWithLimits = new MathSubSuperscript();
currentContext.AppendChild(integralWithLimits);
2026-06-15 14:37:25 +07:00
var baseElem = new MathBase();
var integralRun = CreateMathRun(font);
integralRun.AppendChild(new MathText("∫"));
baseElem.AppendChild(integralRun);
integralWithLimits.AppendChild(baseElem);
if (lowerLimitBuilder is not null)
{
2026-06-15 14:37:25 +07:00
var subArg = new MathSubArgument();
integralWithLimits.AppendChild(subArg);
builder.PushContext(subArg);
lowerLimitBuilder(builder);
builder.PopContext();
}
if (upperLimitBuilder is not null)
{
2026-06-15 14:37:25 +07:00
var superArg = new MathSuperArgument();
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
{
2026-06-15 14:37:25 +07:00
var sumWithLimits = new MathSubSuperscript();
currentContext.AppendChild(sumWithLimits);
2026-06-15 14:37:25 +07:00
var baseElem = new MathBase();
var sumRun = CreateMathRun(font);
sumRun.AppendChild(new MathText("∑"));
baseElem.AppendChild(sumRun);
sumWithLimits.AppendChild(baseElem);
if (lowerLimitBuilder is not null)
{
2026-06-15 14:37:25 +07:00
var subArg = new MathSubArgument();
sumWithLimits.AppendChild(subArg);
builder.PushContext(subArg);
lowerLimitBuilder(builder);
builder.PopContext();
}
if (upperLimitBuilder is not null)
{
2026-06-15 14:37:25 +07:00
var superArg = new MathSuperArgument();
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>
2026-06-15 14:37:25 +07:00
public static MathSuperscript CreateSuperscript(
Action<IFormula> baseBuilder,
Action<IFormula> supBuilder,
IFormula builder)
{
2026-06-15 14:37:25 +07:00
var superscript = new MathSuperscript();
// Основание
2026-06-15 14:37:25 +07:00
var baseElem = new MathBase();
superscript.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
// Показатель
2026-06-15 14:37:25 +07:00
var superArg = new MathSuperArgument();
superscript.AppendChild(superArg);
builder.PushContext(superArg);
supBuilder(builder);
builder.PopContext();
return superscript;
}
/// <summary>Создаёт нижний индекс.</summary>
2026-06-15 14:37:25 +07:00
public static MathSubscript CreateSubscript(
Action<IFormula> baseBuilder,
Action<IFormula> subBuilder,
IFormula builder)
{
2026-06-15 14:37:25 +07:00
var subscript = new MathSubscript();
2026-06-15 14:37:25 +07:00
var baseElem = new MathBase();
subscript.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
2026-06-15 14:37:25 +07:00
var subArg = new MathSubArgument();
subscript.AppendChild(subArg);
builder.PushContext(subArg);
subBuilder(builder);
builder.PopContext();
return subscript;
}
/// <summary>Создаёт одновременные нижний и верхний индексы.</summary>
2026-06-15 14:37:25 +07:00
public static MathSubSuperscript CreateSubSuperscript(
Action<IFormula> baseBuilder,
Action<IFormula> subBuilder,
Action<IFormula> supBuilder,
IFormula builder)
{
2026-06-15 14:37:25 +07:00
var subSup = new MathSubSuperscript();
2026-06-15 14:37:25 +07:00
var baseElem = new MathBase();
subSup.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
2026-06-15 14:37:25 +07:00
var subArg = new MathSubArgument();
subSup.AppendChild(subArg);
builder.PushContext(subArg);
subBuilder(builder);
builder.PopContext();
2026-06-15 14:37:25 +07:00
var superArg = new MathSuperArgument();
subSup.AppendChild(superArg);
builder.PushContext(superArg);
supBuilder(builder);
builder.PopContext();
return subSup;
}
}