using QWERTYkez.WordProcessor.Builders; namespace QWERTYkez.WordProcessor; internal static class ReplaceToTableExt { /// /// Заменяет параграф на таблицу, созданную с помощью TableBuilder /// internal static void ReplaceWithTableBuilder(Paragraph paragraph, Action buildTable, FontProps? baseFont) { if (paragraph is null || paragraph.Parent is null || buildTable is null) return; var parent = paragraph.Parent; // Создаем TableBuilder с базовым шрифтом var builder = TableBuilder.Create(baseFont); buildTable(builder); var table = 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); parent.InsertAt(table, paraIndex); } /// /// Заменяет все параграфы, содержащие указанный текст, на таблицы /// internal static void ReplaceParagraphsContainingTextToTable(Body body, string oldValue, Action buildTable) { if (body is null || string.IsNullOrEmpty(oldValue) || buildTable is null) return; 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) || paraText.IndexOf(oldValue, StringComparison.OrdinalIgnoreCase) < 0) continue; // Извлекаем свойства шрифта для передачи в TableBuilder var fontProps = ExtractFontPropsFromParagraph(paragraph, oldValue); // Заменяем параграф на таблицу ReplaceWithTableBuilder(paragraph, buildTable, fontProps); } } /// /// Извлекает свойства шрифта (FontProps) из Run, содержащего указанный текст в параграфе. /// 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; // Собираем полный текст параграфа и позиции каждого Run для анализа 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; } } // Ищем Run, который содержит искомый текст (регистронезависимо) foreach (var (run, start, end, text) in runInfos) { // Проверяем, содержится ли искомый текст в этом Run 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) { // Находим первый Run, который пересекается с найденным текстом var intersectingRun = runInfos.FirstOrDefault(r => r.Start <= textIndex && r.End > textIndex); if (intersectingRun.Run is not null) { return CreateFontPropsFromRun(intersectingRun.Run); } } return null; // Не удалось найти подходящий Run } /// /// Создаёт FontProps на основе свойств указанного Run. /// private static FontProps CreateFontPropsFromRun(Run run) { var runProperties = run.RunProperties; if (runProperties is null) return new FontProps(); var fontProps = new FontProps(); // Font Family var runFonts = runProperties.GetFirstChild(); if (runFonts is not null) { fontProps = fontProps with { FontFamily = runFonts.Ascii ?? runFonts.HighAnsi ?? runFonts.ComplexScript }; } // Font Size 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 }; } // Bold 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 }; } // Italic 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 }; } // Underline var underline = runProperties.GetFirstChild(); if (underline is not null && underline.Val is not null) { fontProps = fontProps with { Underline = underline.Val.Value }; } // Color var color = runProperties.GetFirstChild(); if (color is not null && !string.IsNullOrEmpty(color.Val)) { fontProps = fontProps with { Color = color.Val }; } // Subscript/Superscript var verticalAlignment = runProperties.GetFirstChild(); if (verticalAlignment is not null && verticalAlignment.Val is not null) { fontProps = fontProps with { SubSup = verticalAlignment.Val.Value }; } return fontProps; } /// /// Вспомогательный метод для получения текста из Run. /// 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(); } }