Files
QWERTYkez.OpenXmlProcessors/QWERTYkez.WordProcessor/ReplaceToTableExt.cs
T
2026-09-24 14:09:37 +07:00

125 lines
5.0 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.
namespace QWERTYkez.WordProcessor;
internal static class ReplaceToTableExt
{
/// <summary>
/// Заменяет параграф на таблицу, созданную с помощью TableBuilder
/// </summary>
internal static void ReplaceWithTableBuilder(Paragraph paragraph, Action<TableBuilder> 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<PageBreakBefore>() 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-ы с <w:br/> — сохраняются (с отрезанным текстом).
ClearParagraphContent(paragraph);
}
// ============================================================
// Очистка содержимого параграфа с сохранением «полезных» элементов.
// ============================================================
private static void ClearParagraphContent(Paragraph paragraph)
{
var toRemove = new List<OpenXmlElement>();
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<Break>().Any())
{
foreach (var t in run.Elements<Text>().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();
}
/// <summary>
/// Заменяет все параграфы, содержащие указанный текст, на таблицы
/// </summary>
internal static void ReplaceParagraphsContainingTextToTable(Body body,
string oldValue, Action<TableBuilder> buildTable, FontProps? baseFont = null)
{
if (body is null || string.IsNullOrEmpty(oldValue) || buildTable is null)
return;
var paragraphs = body.Elements<Paragraph>().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);
}
}
}