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; // 1. Строим таблицу var builder = TableBuilder.Create(baseFont); buildTable(builder); var table = builder.Build(); // 2. Находим индекс параграфа 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; // 3. PageBreakBefore должен сработать ДО таблицы, поэтому снимаем // его с параграфа и выносим в отдельный пустой параграф-маркер. bool hasPageBreakBefore = false; if (paragraph.ParagraphProperties?.GetFirstChild() is { } pbb) { hasPageBreakBefore = true; pbb.Remove(); } int insertAt = paraIndex; if (hasPageBreakBefore) { var pbbPara = new Paragraph(new ParagraphProperties(new PageBreakBefore())); parent.InsertAt(pbbPara, insertAt); insertAt++; } // 4. Вставляем таблицу — параграф при этом сдвигается на +1. // Ссылка на paragraph остаётся валидной. parent.InsertAt(table, insertAt); // 5. Чистим содержимое параграфа: pPr остаётся, текст — удаляется, // run-ы с — сохраняются (с отрезанным текстом). ClearParagraphContent(paragraph); } // ============================================================ // Очистка содержимого параграфа с сохранением «полезных» элементов. // ============================================================ private static void ClearParagraphContent(Paragraph paragraph) { var toRemove = new List(); foreach (var child in paragraph.ChildElements) { if (child is ParagraphProperties) continue; toRemove.Add(child); } foreach (var child in toRemove) { // Run, содержащий page/column break — оставляем, но убираем из него весь текст. if (child is Run run && run.Elements().Any()) { foreach (var t in run.Elements().ToList()) t.Remove(); foreach (var other in run.ChildElements.ToList()) { if (other is Break || other is RunProperties) continue; other.Remove(); } continue; } child.Remove(); } // Если pPr стал пустым — тоже удаляем, чтобы не оставлять мусор. if (paragraph.ParagraphProperties is { } ppr && !ppr.ChildElements.Any()) ppr.Remove(); // Если параграф теперь полностью пуст (нет ни Runs, ни pPr) — убираем его. if (!paragraph.ChildElements.Any()) paragraph.Remove(); } /// /// Заменяет все параграфы, содержащие указанный текст, на таблицы /// internal static void ReplaceParagraphsContainingTextToTable(Body body, string oldValue, Action buildTable, FontProps? baseFont = null) { 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 = paragraph.ExtractFontProps(oldValue); if (fontProps is { } props) fontProps = props.Override(baseFont); // Заменяем параграф на таблицу ReplaceWithTableBuilder(paragraph, buildTable, fontProps); } } }