namespace QWERTYkez.WordProcessor;
///
/// Вспомогательный класс для динамической замены параграфов, содержащих "$key|param$",
/// на текст, построенный через .
///
internal static class DynamicReplaceToTextExt
{
internal static void ReplaceParagraphsContainingTextToTextDynamic(
Body body,
string oldValue,
Action buildText)
{
if (body is null || string.IsNullOrEmpty(oldValue) || buildText is null)
return;
DynamicReplaceExt.ParsePattern(oldValue, out var prefix, out var suffix);
var paragraphs = body.Elements().ToList();
if (paragraphs.Count == 0)
return;
for (int i = paragraphs.Count - 1; i >= 0; i--)
{
var paragraph = paragraphs[i];
if (paragraph is null) continue;
var paraText = paragraph.InnerText;
if (string.IsNullOrEmpty(paraText)) continue;
int pStart = paraText.IndexOf(prefix, StringComparison.OrdinalIgnoreCase);
if (pStart < 0) continue;
int paramStart = pStart + prefix.Length;
int pEnd = string.IsNullOrEmpty(suffix)
? paraText.Length
: paraText.IndexOf(suffix, paramStart, StringComparison.OrdinalIgnoreCase);
if (pEnd < 0) continue;
string param = paraText.Substring(paramStart, pEnd - paramStart);
string matchedText = paraText.Substring(pStart, pEnd + suffix.Length - pStart);
var fontProps = ExtractFontPropsFromParagraph(paragraph, matchedText);
var paragraphProps = paragraph.ParagraphProperties?.CloneNode(true) as ParagraphProperties;
var capturedParam = param;
ReplaceWithTextBuilder(paragraph, tb => buildText(capturedParam, tb), fontProps, paragraphProps);
}
}
// ------------------------------------------------------------
// Ниже — 1:1 копия из ReplaceToTextExt (private helper-методы)
// ------------------------------------------------------------
private static void ReplaceWithTextBuilder(
Paragraph paragraph,
Action buildText,
FontProps? baseFont,
ParagraphProperties? baseParagraphProps)
{
if (paragraph is null || paragraph.Parent is null || buildText is null)
return;
var parent = paragraph.Parent;
var builder = TextBuilder.Create(baseFont, baseParagraphProps);
buildText(builder);
var newParagraphs = builder.Build();
int paraIndex = -1;
var children = parent.ChildElements;
for (int i = 0; i < children.Count; i++)
{
if (children[i] == paragraph)
{
paraIndex = i;
break;
}
}
if (paraIndex == -1)
return;
parent.RemoveChild(paragraph);
for (int i = 0; i < newParagraphs.Count; i++)
{
parent.InsertAt(newParagraphs[i], paraIndex + i);
}
}
private static FontProps? ExtractFontPropsFromParagraph(Paragraph paragraph, string searchText)
{
if (paragraph is null || string.IsNullOrEmpty(searchText))
return null;
var runs = paragraph.Descendants().ToList();
if (runs.Count == 0)
return null;
var runInfos = new List<(Run Run, int Start, int End, string Text)>();
int currentPosition = 0;
foreach (var run in runs)
{
var runText = GetRunText(run);
if (!string.IsNullOrEmpty(runText))
{
runInfos.Add((run, currentPosition, currentPosition + runText.Length, runText));
currentPosition += runText.Length;
}
}
foreach (var (run, start, end, text) in runInfos)
{
if (text.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0)
{
return CreateFontPropsFromRun(run);
}
}
var fullText = string.Concat(runInfos.Select(r => r.Text));
int textIndex = fullText.IndexOf(searchText, StringComparison.OrdinalIgnoreCase);
if (textIndex >= 0)
{
var intersectingRun = runInfos.FirstOrDefault(r =>
r.Start <= textIndex && r.End > textIndex);
if (intersectingRun.Run is not null)
{
return CreateFontPropsFromRun(intersectingRun.Run);
}
}
return null;
}
private static FontProps CreateFontPropsFromRun(Run run)
{
var runProperties = run.RunProperties;
if (runProperties is null)
return new FontProps();
var fontProps = new FontProps();
var runFonts = runProperties.GetFirstChild();
if (runFonts is not null)
{
fontProps = fontProps with
{
FontFamily = runFonts.Ascii ?? runFonts.HighAnsi ?? runFonts.ComplexScript
};
}
var fontSize = runProperties.GetFirstChild();
if (fontSize is not null && !string.IsNullOrEmpty(fontSize.Val) &&
uint.TryParse(fontSize.Val, out uint halfPoints))
{
fontProps = fontProps with { Size = halfPoints / 2.0 };
}
var bold = runProperties.GetFirstChild();
if (bold is not null)
{
bool isBold = true;
if (bold.Val is not null)
isBold = bold.Val.Value;
fontProps = fontProps with { IsBold = isBold };
}
var italic = runProperties.GetFirstChild();
if (italic is not null)
{
bool isItalic = true;
if (italic.Val is not null)
isItalic = italic.Val.Value;
fontProps = fontProps with { IsItalic = isItalic };
}
var underline = runProperties.GetFirstChild();
if (underline is not null && underline.Val is not null)
{
fontProps = fontProps with { Underline = underline.Val.Value };
}
var color = runProperties.GetFirstChild();
if (color is not null && !string.IsNullOrEmpty(color.Val))
{
fontProps = fontProps with { Color = color.Val };
}
var verticalAlignment = runProperties.GetFirstChild();
if (verticalAlignment is not null && verticalAlignment.Val is not null)
{
fontProps = fontProps with { SubSup = verticalAlignment.Val.Value };
}
return fontProps;
}
private static string GetRunText(Run run)
{
if (run is null) return string.Empty;
var sb = new StringBuilder();
foreach (var text in run.Elements())
{
sb.Append(text.Text ?? string.Empty);
}
return sb.ToString();
}
}