Files
QWERTYkez.OpenXmlProcessors/QWERTYkez.WordProcessor/HeaderFooterProcessor.cs

371 lines
13 KiB
C#
Raw Normal View History

namespace QWERTYkez.WordProcessor;
/// <summary>
/// Обработчик текста в верхних и нижних колонтитулах DOCX документов
/// </summary>
internal static class HeaderFooterProcessor
{
/// <summary>
/// Заменяет текст во всех колонтитулах документа
/// </summary>
internal static void ReplaceInHeadersFooters(WordprocessingDocument document, string oldValue, IEnumerable<string> 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);
}
}
/// <summary>
/// Выполняет словарную замену во всех колонтитулах
/// </summary>
internal static void ReplaceInHeadersFooters(WordprocessingDocument document, IEnumerable<KeyValuePair<string, IEnumerable<string>>> 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);
}
}
/// <summary>
/// Выполняет словарную замену во всех колонтитулах
/// </summary>
internal static void ReplaceInHeadersFooters(WordprocessingDocument document, IEnumerable<KeyValuePair<string, string>> 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);
}
}
/// <summary>
/// Заменяет текст в указанном элементе (Header или Footer)
/// </summary>
private static void ReplaceInElement(OpenXmlElement element, string oldValue, IEnumerable<string> newValues, StringComparison comparisonType)
{
if (element is null)
return;
var paragraphs = element.Descendants<Paragraph>().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);
}
}
}
}
/// <summary>
/// Выполняет словарную замену в указанном элементе
/// </summary>
private static void ReplaceInElement(OpenXmlElement element, IEnumerable<KeyValuePair<string, IEnumerable<string>>> replacements, StringComparison comparisonType)
{
if (element is null || replacements is null || !replacements.Any())
return;
var paragraphs = element.Descendants<Paragraph>().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);
}
}
}
/// <summary>
/// Выполняет словарную замену в указанном элементе
/// </summary>
private static void ReplaceInElement(OpenXmlElement element, IEnumerable<KeyValuePair<string, string>> replacements, StringComparison comparisonType)
{
if (element is null || replacements is null || !replacements.Any())
return;
var paragraphs = element.Descendants<Paragraph>().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
// Остальные методы остаются без изменений
/// <summary>
/// Находит и возвращает все верхние колонтитулы в документе
/// </summary>
internal static IEnumerable<OpenXmlElement> 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;
}
}
}
/// <summary>
/// Находит и возвращает все нижние колонтитулы в документе
/// </summary>
internal static IEnumerable<OpenXmlElement> 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;
}
}
}
/// <summary>
/// Проверяет, содержит ли документ колонтитулы
/// </summary>
internal static bool HasHeadersOrFooters(WordprocessingDocument document)
{
if (document?.MainDocumentPart is null)
return false;
return document.MainDocumentPart.HeaderParts.Any() || document.MainDocumentPart.FooterParts.Any();
}
/// <summary>
/// Заменяет параграф на новые параграфы в родительском элементе
/// </summary>
private static void ReplaceParagraphInParent(Paragraph oldParagraph, List<Paragraph> newParagraphs)
{
ParagraphReplacer.ReplaceParagraph(oldParagraph, newParagraphs);
}
}