namespace QWERTYkez.WordProcessor; /// /// Обработчик текста в верхних и нижних колонтитулах DOCX документов /// internal static class HeaderFooterProcessor { /// /// Заменяет текст во всех колонтитулах документа /// internal static void ReplaceInHeadersFooters(WordprocessingDocument document, string oldValue, IEnumerable newValues, StringComparison comparisonType) { if (document is null || string.IsNullOrEmpty(oldValue) || newValues is null) return; var headers = GetAllHeaders(document).ToList(); var footers = GetAllFooters(document).ToList(); if (headers.Count == 0 && footers.Count == 0) return; #if DEBUG int headerMatches = 0; int footerMatches = 0; foreach (var header in headers) { if (ContainsText(header, oldValue, comparisonType)) { headerMatches++; } } foreach (var footer in footers) { if (ContainsText(footer, oldValue, comparisonType)) { footerMatches++; } } if (headerMatches > 0 || footerMatches > 0) { Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters] Looking for '{oldValue}'"); Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters] Headers: {headers.Count} total, {headerMatches} with matches"); Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters] Footers: {footers.Count} total, {footerMatches} with matches"); } #endif foreach (var header in headers) { ReplaceInElement(header, oldValue, newValues, comparisonType); } foreach (var footer in footers) { ReplaceInElement(footer, oldValue, newValues, comparisonType); } } /// /// Выполняет словарную замену во всех колонтитулах /// internal static void ReplaceInHeadersFooters(WordprocessingDocument document, IEnumerable>> replacements, StringComparison comparisonType) { if (document is null || replacements is null || !replacements.Any()) return; var headers = GetAllHeaders(document).ToList(); var footers = GetAllFooters(document).ToList(); if (headers.Count == 0 && footers.Count == 0) return; #if DEBUG int headerMatches = 0; int footerMatches = 0; foreach (var header in headers) { foreach (var kvp in replacements) { if (!string.IsNullOrEmpty(kvp.Key) && ContainsText(header, kvp.Key, comparisonType)) { headerMatches++; break; } } } foreach (var footer in footers) { foreach (var kvp in replacements) { if (!string.IsNullOrEmpty(kvp.Key) && ContainsText(footer, kvp.Key, comparisonType)) { footerMatches++; break; } } } if (headerMatches > 0 || footerMatches > 0) { Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters(dict)] START with {replacements.Count()} items"); Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters(dict)] Headers: {headers.Count} total, {headerMatches} with matches"); Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters(dict)] Footers: {footers.Count} total, {footerMatches} with matches"); } #endif foreach (var header in headers) { ReplaceInElement(header, replacements, comparisonType); } foreach (var footer in footers) { ReplaceInElement(footer, replacements, comparisonType); } } /// /// Выполняет словарную замену во всех колонтитулах /// internal static void ReplaceInHeadersFooters(WordprocessingDocument document, IEnumerable> replacements, StringComparison comparisonType) { if (document is null || replacements is null || !replacements.Any()) return; var headers = GetAllHeaders(document).ToList(); var footers = GetAllFooters(document).ToList(); if (headers.Count == 0 && footers.Count == 0) return; #if DEBUG int headerMatches = 0; int footerMatches = 0; foreach (var header in headers) { foreach (var kvp in replacements) { if (!string.IsNullOrEmpty(kvp.Key) && ContainsText(header, kvp.Key, comparisonType)) { headerMatches++; break; } } } foreach (var footer in footers) { foreach (var kvp in replacements) { if (!string.IsNullOrEmpty(kvp.Key) && ContainsText(footer, kvp.Key, comparisonType)) { footerMatches++; break; } } } if (headerMatches > 0 || footerMatches > 0) { Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters(dict)] START with {replacements.Count()} items"); Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters(dict)] Headers: {headers.Count} total, {headerMatches} with matches"); Debug.WriteLine($"[DEBUG] [HeaderFooterProcessor.ReplaceInHeadersFooters(dict)] Footers: {footers.Count} total, {footerMatches} with matches"); } #endif foreach (var header in headers) { ReplaceInElement(header, replacements, comparisonType); } foreach (var footer in footers) { ReplaceInElement(footer, replacements, comparisonType); } } /// /// Заменяет текст в указанном элементе (Header или Footer) /// private static void ReplaceInElement(OpenXmlElement element, string oldValue, IEnumerable newValues, StringComparison comparisonType) { if (element is null) return; var paragraphs = element.Descendants().ToList(); if (paragraphs.Count == 0) return; var tables = TableTextProcessor.GetAllTables(element).ToList(); var tableParagraphs = tables.SelectMany(t => TableTextProcessor.GetTableParagraphs(t)).ToList(); var allParagraphs = paragraphs.Concat(tableParagraphs).Distinct().ToList(); if (newValues.Count() == 1) { foreach (var paragraph in allParagraphs) { paragraph?.SimpleReplace(oldValue, newValues.First(), comparisonType); } } else { foreach (var paragraph in allParagraphs) { if (paragraph is not null && paragraph.InnerText.IndexOf(oldValue, comparisonType) >= 0) { paragraph.ReplaceWithMultiple(oldValue, newValues, comparisonType); } } } } /// /// Выполняет словарную замену в указанном элементе /// private static void ReplaceInElement(OpenXmlElement element, IEnumerable>> replacements, StringComparison comparisonType) { if (element is null || replacements is null || !replacements.Any()) return; var paragraphs = element.Descendants().ToList(); if (paragraphs.Count == 0) return; var tables = TableTextProcessor.GetAllTables(element).ToList(); var tableParagraphs = tables.SelectMany(t => TableTextProcessor.GetTableParagraphs(t)).ToList(); var allParagraphs = paragraphs.Concat(tableParagraphs).Distinct().ToList(); foreach (var paragraph in allParagraphs) { if (paragraph is null) continue; var paraText = paragraph.InnerText; bool hasMatch = false; foreach (var kvp in replacements) { if (!string.IsNullOrEmpty(kvp.Key) && paraText.IndexOf(kvp.Key, comparisonType) >= 0) { hasMatch = true; break; } } if (!hasMatch) continue; var newParagraphs = MultiReplaceExt.ProcessParagraphWithAllReplacements(paragraph, replacements, comparisonType); if (newParagraphs is not null && newParagraphs.Count > 0) { ReplaceParagraphInParent(paragraph, newParagraphs); } } } /// /// Выполняет словарную замену в указанном элементе /// private static void ReplaceInElement(OpenXmlElement element, IEnumerable> replacements, StringComparison comparisonType) { if (element is null || replacements is null || !replacements.Any()) return; var paragraphs = element.Descendants().ToList(); if (paragraphs.Count == 0) return; var tables = TableTextProcessor.GetAllTables(element).ToList(); var tableParagraphs = tables.SelectMany(t => TableTextProcessor.GetTableParagraphs(t)).ToList(); var allParagraphs = paragraphs.Concat(tableParagraphs).Distinct().ToList(); foreach (var paragraph in allParagraphs) { if (paragraph is null) continue; var paraText = paragraph.InnerText; bool hasMatch = false; foreach (var kvp in replacements) { if (!string.IsNullOrEmpty(kvp.Key) && paraText.IndexOf(kvp.Key, comparisonType) >= 0) { hasMatch = true; break; } } if (!hasMatch) continue; var newParagraphs = MultiReplaceExt.ProcessParagraphWithAllReplacements(paragraph, replacements, comparisonType); if (newParagraphs is not null && newParagraphs.Count > 0) { ReplaceParagraphInParent(paragraph, newParagraphs); } } } #if DEBUG private static bool ContainsText(OpenXmlElement element, string searchText, StringComparison comparisonType) { if (element is null || string.IsNullOrEmpty(searchText)) return false; return element.InnerText?.IndexOf(searchText, comparisonType) >= 0; } #endif // Остальные методы остаются без изменений /// /// Находит и возвращает все верхние колонтитулы в документе /// internal static IEnumerable GetAllHeaders(WordprocessingDocument document) { if (document?.MainDocumentPart is null) yield break; foreach (var headerPart in document.MainDocumentPart.HeaderParts) { if (headerPart?.Header is not null) { yield return headerPart.Header; } } } /// /// Находит и возвращает все нижние колонтитулы в документе /// internal static IEnumerable GetAllFooters(WordprocessingDocument document) { if (document?.MainDocumentPart is null) yield break; foreach (var footerPart in document.MainDocumentPart.FooterParts) { if (footerPart?.Footer is not null) { yield return footerPart.Footer; } } } /// /// Проверяет, содержит ли документ колонтитулы /// internal static bool HasHeadersOrFooters(WordprocessingDocument document) { if (document?.MainDocumentPart is null) return false; return document.MainDocumentPart.HeaderParts.Any() || document.MainDocumentPart.FooterParts.Any(); } /// /// Заменяет параграф на новые параграфы в родительском элементе /// private static void ReplaceParagraphInParent(Paragraph oldParagraph, List newParagraphs) { ParagraphReplacer.ReplaceParagraph(oldParagraph, newParagraphs); } }