This commit is contained in:
melekhin
2026-09-01 13:45:51 +07:00
parent fa77b2ce39
commit 7fec0ea1bf
+65 -36
View File
@@ -180,7 +180,6 @@ internal static class MultiReplaceExt
return sb.ToString(); return sb.ToString();
} }
/// <summary>Клонирует параграф, удаляя все SectionProperties и PageBreakBefore.</summary>
private static Paragraph CloneParagraphWithoutSection(Paragraph original) private static Paragraph CloneParagraphWithoutSection(Paragraph original)
{ {
var newPara = new Paragraph(); var newPara = new Paragraph();
@@ -367,13 +366,12 @@ internal static class MultiReplaceExt
if (!(sourceIsLandscape && sectionChangeInsideGroup && lastIsPortrait)) if (!(sourceIsLandscape && sectionChangeInsideGroup && lastIsPortrait))
return; return;
foreach (var bodySec in body.Elements<SectionProperties>()) // Удаляем все существующие секции из Body
{ var existingSections = body.Elements<SectionProperties>().ToList();
var ps = bodySec.GetFirstChild<PageSize>(); foreach (var sec in existingSections)
if (ps is not null && ps.Orient?.Value == PageOrientationValues.Landscape) sec.Remove();
return;
}
// Создаём новую landscape секцию для Body
var bodySection = new SectionProperties(); var bodySection = new SectionProperties();
var pageSizeClone = (PageSize)sourcePageSize.CloneNode(true); var pageSizeClone = (PageSize)sourcePageSize.CloneNode(true);
if (pageSizeClone.Orient?.Value != PageOrientationValues.Landscape) if (pageSizeClone.Orient?.Value != PageOrientationValues.Landscape)
@@ -404,7 +402,6 @@ internal static class MultiReplaceExt
// ---------- ОСНОВНОЙ АЛГОРИТМ ---------- // ---------- ОСНОВНОЙ АЛГОРИТМ ----------
/// <summary>Основной алгоритм множественной замены с поддержкой разрывов и смены ориентации.</summary>
private static List<Paragraph>? ProcessMultiReplacements( private static List<Paragraph>? ProcessMultiReplacements(
Paragraph original, Paragraph original,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? stringReplacements, IEnumerable<KeyValuePair<string, IEnumerable<string>>>? stringReplacements,
@@ -424,6 +421,22 @@ internal static class MultiReplaceExt
prevPara = prevPara.PreviousSibling<Paragraph>(); prevPara = prevPara.PreviousSibling<Paragraph>();
} }
// Определяем секцию, которая следует за original (если у original нет секции)
SectionProperties? followingSection = null;
if (original.ParagraphProperties?.GetFirstChild<SectionProperties>()?.CloneNode(true) is not SectionProperties sourceSection)
{
var nextPara = original.NextSibling<Paragraph>();
if (nextPara is not null)
{
followingSection = nextPara.ParagraphProperties?.GetFirstChild<SectionProperties>()?.CloneNode(true) as SectionProperties;
}
}
else
{
followingSection = sourceSection;
}
followingSection ??= new SectionProperties();
// 1. Сбор определений // 1. Сбор определений
var definitions = new List<MatchDefinition>(); var definitions = new List<MatchDefinition>();
if (stringReplacements is not null) if (stringReplacements is not null)
@@ -471,14 +484,7 @@ internal static class MultiReplaceExt
if (matches.Count == 0) return null; if (matches.Count == 0) return null;
matches.Sort((a, b) => a.Start.CompareTo(b.Start)); matches.Sort((a, b) => a.Start.CompareTo(b.Start));
// 4. Получаем исходную секцию для копирования (из original или из документа) // 4. Получаем книжную секцию для копирования полей (из документа)
SectionProperties? sourceSection = original.ParagraphProperties?.GetFirstChild<SectionProperties>()?.CloneNode(true) as SectionProperties;
if (sourceSection is null && body is not null)
{
sourceSection = body.Elements<SectionProperties>().LastOrDefault()?.CloneNode(true) as SectionProperties;
}
sourceSection ??= new SectionProperties();
SectionProperties? portraitSection = null; SectionProperties? portraitSection = null;
if (body is not null) if (body is not null)
{ {
@@ -539,6 +545,29 @@ internal static class MultiReplaceExt
var values = match.Definition.Values.ToList(); var values = match.Definition.Values.ToList();
if (values.Count == 0) continue; if (values.Count == 0) continue;
// Если это простая замена текста (один элемент и это текст без маркеров),
// заменяем текст внутри параграфа, сохраняя все остальные элементы
if (values.Count == 1 && !string.IsNullOrEmpty(values[0].Text) && !values[0].BreakValue.HasValue)
{
// Простая замена текста в текущем параграфе
var newText = values[0].Text;
// Находим сегмент с совпадением
var matchSeg = structure.Segments.FirstOrDefault(s => s.Start >= match.Start && s.End <= match.End);
if (matchSeg is not null)
{
// Заменяем текст в Run
var run = matchSeg.Run;
// Удаляем все Text элементы
foreach (var textElem in run.Elements<Text>().ToList())
textElem.Remove();
// Добавляем новый Text
run.AppendChild(new Text(newText));
// Возвращаем null, чтобы не заменять параграф
return null;
}
// Если не нашли сегмент, продолжаем обычную обработку
}
// Сбрасываем флаги для новой группы // Сбрасываем флаги для новой группы
pageBreakBeforeNext = false; pageBreakBeforeNext = false;
pendingOrientation = null; pendingOrientation = null;
@@ -563,7 +592,7 @@ internal static class MultiReplaceExt
original, original,
structure, structure,
match.Start, match.Start,
sourceSection, followingSection,
portraitSection, portraitSection,
resultParas); resultParas);
} }
@@ -582,7 +611,7 @@ internal static class MultiReplaceExt
ref emptyParagraphCreated, ref emptyParagraphCreated,
hasEmptyParagraphsBefore, hasEmptyParagraphsBefore,
original, original,
sourceSection, followingSection,
portraitSection, portraitSection,
resultParas); resultParas);
} }
@@ -594,26 +623,26 @@ internal static class MultiReplaceExt
if (currentPara.ParagraphProperties?.GetFirstChild<SectionProperties>() is null) if (currentPara.ParagraphProperties?.GetFirstChild<SectionProperties>() is null)
{ {
bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection); bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(currentPara, lastOrientation.Value, addPageSize, sourceSection, portraitSection); AddSectionProperties(currentPara, lastOrientation.Value, addPageSize, followingSection, portraitSection);
} }
} }
// Если внутри группы не было смены, но есть sourceSection с PageSize, // Если внутри группы не было смены, но есть followingSection с PageSize,
// копируем её в последний параграф группы (если он не имеет секции) // копируем её в последний параграф группы (если он не имеет секции)
if (!sectionChangeInsideGroup && currentPara is not null) if (!sectionChangeInsideGroup && currentPara is not null)
{ {
var sourcePageSize = sourceSection.GetFirstChild<PageSize>(); var sourcePageSize = followingSection.GetFirstChild<PageSize>();
if (sourcePageSize is not null) if (sourcePageSize is not null)
{ {
if (currentPara.ParagraphProperties?.GetFirstChild<SectionProperties>() is null) if (currentPara.ParagraphProperties?.GetFirstChild<SectionProperties>() is null)
{ {
currentPara.ParagraphProperties ??= new ParagraphProperties(); currentPara.ParagraphProperties ??= new ParagraphProperties();
currentPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true)); currentPara.ParagraphProperties.AppendChild(followingSection.CloneNode(true));
} }
} }
} }
EnsureBodyLandscapeSection(body!, sourceSection, sectionChangeInsideGroup, lastOrientation); EnsureBodyLandscapeSection(body!, followingSection, sectionChangeInsideGroup, lastOrientation);
currentPos = match.End; currentPos = match.End;
} }
@@ -630,7 +659,7 @@ internal static class MultiReplaceExt
var breakRun = new Run(new Break { Type = BreakValues.Page }); var breakRun = new Run(new Break { Type = BreakValues.Page });
remainderPara.InsertAt(breakRun, 0); remainderPara.InsertAt(breakRun, 0);
bool addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection); bool addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(remainderPara, pendingOrientation.Value, addPageSize, sourceSection, portraitSection); AddSectionProperties(remainderPara, pendingOrientation.Value, addPageSize, followingSection, portraitSection);
pendingOrientation = null; pendingOrientation = null;
} }
else if (sectionChangeInsideGroup && lastOrientation.HasValue) else if (sectionChangeInsideGroup && lastOrientation.HasValue)
@@ -638,16 +667,16 @@ internal static class MultiReplaceExt
var breakRun = new Run(new Break { Type = BreakValues.Page }); var breakRun = new Run(new Break { Type = BreakValues.Page });
remainderPara.InsertAt(breakRun, 0); remainderPara.InsertAt(breakRun, 0);
bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection); bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(remainderPara, lastOrientation.Value, addPageSize, sourceSection, portraitSection); AddSectionProperties(remainderPara, lastOrientation.Value, addPageSize, followingSection, portraitSection);
} }
else else
{ {
// Если нет смены, но есть sourceSection с PageSize, копируем её в остаток // Если нет смены, но есть followingSection с PageSize, копируем её в остаток
var sourcePageSize = sourceSection.GetFirstChild<PageSize>(); var sourcePageSize = followingSection.GetFirstChild<PageSize>();
if (sourcePageSize is not null) if (sourcePageSize is not null)
{ {
remainderPara.ParagraphProperties ??= new ParagraphProperties(); remainderPara.ParagraphProperties ??= new ParagraphProperties();
remainderPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true)); remainderPara.ParagraphProperties.AppendChild(followingSection.CloneNode(true));
} }
} }
resultParas.Add(remainderPara); resultParas.Add(remainderPara);
@@ -679,7 +708,7 @@ internal static class MultiReplaceExt
Paragraph original, Paragraph original,
ParagraphStructure structure, ParagraphStructure structure,
int position, int position,
SectionProperties sourceSection, SectionProperties followingSection,
SectionProperties portraitSection, SectionProperties portraitSection,
List<Paragraph> resultParas) List<Paragraph> resultParas)
{ {
@@ -690,7 +719,7 @@ internal static class MultiReplaceExt
currentPara = newPara; currentPara = newPara;
bool addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection); bool addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(currentPara, pendingOrientation.Value, addPageSize, sourceSection, portraitSection); AddSectionProperties(currentPara, pendingOrientation.Value, addPageSize, followingSection, portraitSection);
lastOrientation = pendingOrientation; lastOrientation = pendingOrientation;
sectionChangeInsideGroup = true; sectionChangeInsideGroup = true;
pendingOrientation = null; pendingOrientation = null;
@@ -738,7 +767,7 @@ internal static class MultiReplaceExt
ref bool emptyParagraphCreated, ref bool emptyParagraphCreated,
bool hasEmptyParagraphsBefore, bool hasEmptyParagraphsBefore,
Paragraph original, Paragraph original,
SectionProperties sourceSection, SectionProperties followingSection,
SectionProperties portraitSection, SectionProperties portraitSection,
List<Paragraph> resultParas) List<Paragraph> resultParas)
{ {
@@ -764,7 +793,7 @@ internal static class MultiReplaceExt
var newPara = CloneParagraphWithoutSection(original); var newPara = CloneParagraphWithoutSection(original);
resultParas.Add(newPara); resultParas.Add(newPara);
currentPara = newPara; currentPara = newPara;
AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection); AddSectionProperties(currentPara, breakType, false, followingSection, portraitSection);
lastOrientation = breakType; lastOrientation = breakType;
sectionChangeInsideGroup = true; sectionChangeInsideGroup = true;
firstSplitMarkerHandled = true; firstSplitMarkerHandled = true;
@@ -788,14 +817,14 @@ internal static class MultiReplaceExt
{ {
lastTextPara.ParagraphProperties ??= new ParagraphProperties(); lastTextPara.ParagraphProperties ??= new ParagraphProperties();
var sourcePageSize = sourceSection.GetFirstChild<PageSize>(); var sourcePageSize = followingSection.GetFirstChild<PageSize>();
if (sourcePageSize is not null) if (sourcePageSize is not null)
{ {
lastTextPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true)); lastTextPara.ParagraphProperties.AppendChild(followingSection.CloneNode(true));
} }
else else
{ {
AddSectionProperties(lastTextPara, breakType, false, sourceSection, portraitSection); AddSectionProperties(lastTextPara, breakType, false, followingSection, portraitSection);
} }
lastOrientation = breakType; lastOrientation = breakType;
sectionChangeInsideGroup = true; sectionChangeInsideGroup = true;
@@ -805,7 +834,7 @@ internal static class MultiReplaceExt
} }
else else
{ {
AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection); AddSectionProperties(currentPara, breakType, false, followingSection, portraitSection);
lastOrientation = breakType; lastOrientation = breakType;
sectionChangeInsideGroup = true; sectionChangeInsideGroup = true;
firstSplitMarkerHandled = true; firstSplitMarkerHandled = true;