Добавьте файлы проекта.
This commit is contained in:
357
QWERTYkez.WordProcessor/Builders/FormulaHelper.cs
Normal file
357
QWERTYkez.WordProcessor/Builders/FormulaHelper.cs
Normal file
@@ -0,0 +1,357 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user