Files
QWERTYkez.OpenXmlProcessors/QWERTYkez.WordProcessor/Builders/FormulaHelper.cs
melekhin 282be475d5
All checks were successful
Publish NuGet packages / publish (push) Successful in 27s
Formula Debug
2026-06-15 14:37:25 +07:00

357 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
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;
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 MathFraction CreateFraction(
Action<IFormula> numeratorBuilder,
Action<IFormula> denominatorBuilder,
IFormula builder)
{
var fraction = new MathFraction();
var font = builder.BaseFont;
// Добавляем свойства дроби с форматированием (для черты дроби)
var fPr = new MathFractionProperties();
if (font.TryExtractWithoutFamily(out var wordElements))
{
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);
// Числитель
var numeratorElem = new MathNumerator();
fraction.AppendChild(numeratorElem);
builder.PushContext(numeratorElem);
numeratorBuilder(builder);
builder.PopContext();
// Знаменатель
var denominatorElem = new MathDenominator();
fraction.AppendChild(denominatorElem);
builder.PushContext(denominatorElem);
denominatorBuilder(builder);
builder.PopContext();
return fraction;
}
public static MathRadical CreateRadical(
Action<IFormula> radicandBuilder,
Action<IFormula>? degreeBuilder,
IFormula builder)
{
var radical = new MathRadical();
var font = builder.BaseFont;
var radPr = new MathRadicalProperties();
// Цвет, размер и подчёркивание для знака корня (ControlProperties)
if (font is not null && font.TryExtractWithoutFamily(out var wordElements))
{
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)
{
radPr.HideDegree = new MathHideDegree { Val = DocumentFormat.OpenXml.Math.BooleanValues.One };
}
// Добавляем свойства радикала (с цветом)
radical.AppendChild(radPr);
// Степень (если есть)
if (degreeBuilder is not null)
{
var degree = new MathDegree();
radical.AppendChild(degree);
builder.PushContext(degree);
degreeBuilder(builder);
builder.PopContext();
}
// Подкоренное выражение
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
{
var integralWithLimits = new MathSubSuperscript();
currentContext.AppendChild(integralWithLimits);
var baseElem = new MathBase();
var integralRun = CreateMathRun(font);
integralRun.AppendChild(new MathText("∫"));
baseElem.AppendChild(integralRun);
integralWithLimits.AppendChild(baseElem);
if (lowerLimitBuilder is not null)
{
var subArg = new MathSubArgument();
integralWithLimits.AppendChild(subArg);
builder.PushContext(subArg);
lowerLimitBuilder(builder);
builder.PopContext();
}
if (upperLimitBuilder is not null)
{
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
{
var sumWithLimits = new MathSubSuperscript();
currentContext.AppendChild(sumWithLimits);
var baseElem = new MathBase();
var sumRun = CreateMathRun(font);
sumRun.AppendChild(new MathText("∑"));
baseElem.AppendChild(sumRun);
sumWithLimits.AppendChild(baseElem);
if (lowerLimitBuilder is not null)
{
var subArg = new MathSubArgument();
sumWithLimits.AppendChild(subArg);
builder.PushContext(subArg);
lowerLimitBuilder(builder);
builder.PopContext();
}
if (upperLimitBuilder is not null)
{
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>
public static MathSuperscript CreateSuperscript(
Action<IFormula> baseBuilder,
Action<IFormula> supBuilder,
IFormula builder)
{
var superscript = new MathSuperscript();
// Основание
var baseElem = new MathBase();
superscript.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
// Показатель
var superArg = new MathSuperArgument();
superscript.AppendChild(superArg);
builder.PushContext(superArg);
supBuilder(builder);
builder.PopContext();
return superscript;
}
/// <summary>Создаёт нижний индекс.</summary>
public static MathSubscript CreateSubscript(
Action<IFormula> baseBuilder,
Action<IFormula> subBuilder,
IFormula builder)
{
var subscript = new MathSubscript();
var baseElem = new MathBase();
subscript.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
var subArg = new MathSubArgument();
subscript.AppendChild(subArg);
builder.PushContext(subArg);
subBuilder(builder);
builder.PopContext();
return subscript;
}
/// <summary>Создаёт одновременные нижний и верхний индексы.</summary>
public static MathSubSuperscript CreateSubSuperscript(
Action<IFormula> baseBuilder,
Action<IFormula> subBuilder,
Action<IFormula> supBuilder,
IFormula builder)
{
var subSup = new MathSubSuperscript();
var baseElem = new MathBase();
subSup.AppendChild(baseElem);
builder.PushContext(baseElem);
baseBuilder(builder);
builder.PopContext();
var subArg = new MathSubArgument();
subSup.AppendChild(subArg);
builder.PushContext(subArg);
subBuilder(builder);
builder.PopContext();
var superArg = new MathSuperArgument();
subSup.AppendChild(superArg);
builder.PushContext(superArg);
supBuilder(builder);
builder.PopContext();
return subSup;
}
}