357 lines
12 KiB
C#
357 lines
12 KiB
C#
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.Builders;
|
||
|
||
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<IFormula> numeratorBuilder,
|
||
Action<IFormula> 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<IFormula> radicandBuilder,
|
||
Action<IFormula>? 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<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
|
||
{
|
||
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<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
|
||
{
|
||
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();
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>Создаёт степень.</summary>
|
||
public static Superscript CreateSuperscript(
|
||
Action<IFormula> baseBuilder,
|
||
Action<IFormula> 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;
|
||
}
|
||
|
||
/// <summary>Создаёт нижний индекс.</summary>
|
||
public static Subscript CreateSubscript(
|
||
Action<IFormula> baseBuilder,
|
||
Action<IFormula> 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;
|
||
}
|
||
|
||
/// <summary>Создаёт одновременные нижний и верхний индексы.</summary>
|
||
public static SubSuperscript CreateSubSuperscript(
|
||
Action<IFormula> baseBuilder,
|
||
Action<IFormula> subBuilder,
|
||
Action<IFormula> 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;
|
||
}
|
||
} |