Добавьте файлы проекта.
This commit is contained in:
371
QWERTYkez.WordProcessor/HeaderFooterProcessor.cs
Normal file
371
QWERTYkez.WordProcessor/HeaderFooterProcessor.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user