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();
}
/// <summary>Клонирует параграф, удаляя все SectionProperties и PageBreakBefore.</summary>
private static Paragraph CloneParagraphWithoutSection(Paragraph original)
{
var newPara = new Paragraph();
@@ -367,13 +366,12 @@ internal static class MultiReplaceExt
if (!(sourceIsLandscape && sectionChangeInsideGroup && lastIsPortrait))
return;
foreach (var bodySec in body.Elements<SectionProperties>())
{
var ps = bodySec.GetFirstChild<PageSize>();
if (ps is not null && ps.Orient?.Value == PageOrientationValues.Landscape)
return;
}
// Удаляем все существующие секции из Body
var existingSections = body.Elements<SectionProperties>().ToList();
foreach (var sec in existingSections)
sec.Remove();
// Создаём новую landscape секцию для Body
var bodySection = new SectionProperties();
var pageSizeClone = (PageSize)sourcePageSize.CloneNode(true);
if (pageSizeClone.Orient?.Value != PageOrientationValues.Landscape)
@@ -404,7 +402,6 @@ internal static class MultiReplaceExt
// ---------- ОСНОВНОЙ АЛГОРИТМ ----------
/// <summary>Основной алгоритм множественной замены с поддержкой разрывов и смены ориентации.</summary>
private static List<Paragraph>? ProcessMultiReplacements(
Paragraph original,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? stringReplacements,
@@ -424,6 +421,22 @@ internal static class MultiReplaceExt
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. Сбор определений
var definitions = new List<MatchDefinition>();
if (stringReplacements is not null)
@@ -471,14 +484,7 @@ internal static class MultiReplaceExt
if (matches.Count == 0) return null;
matches.Sort((a, b) => a.Start.CompareTo(b.Start));
// 4. Получаем исходную секцию для копирования (из original или из документа)
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();
// 4. Получаем книжную секцию для копирования полей (из документа)
SectionProperties? portraitSection = null;
if (body is not null)
{
@@ -539,6 +545,29 @@ internal static class MultiReplaceExt
var values = match.Definition.Values.ToList();
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;
pendingOrientation = null;
@@ -563,7 +592,7 @@ internal static class MultiReplaceExt
original,
structure,
match.Start,
sourceSection,
followingSection,
portraitSection,
resultParas);
}
@@ -582,7 +611,7 @@ internal static class MultiReplaceExt
ref emptyParagraphCreated,
hasEmptyParagraphsBefore,
original,
sourceSection,
followingSection,
portraitSection,
resultParas);
}
@@ -594,26 +623,26 @@ internal static class MultiReplaceExt
if (currentPara.ParagraphProperties?.GetFirstChild<SectionProperties>() is null)
{
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)
{
var sourcePageSize = sourceSection.GetFirstChild<PageSize>();
var sourcePageSize = followingSection.GetFirstChild<PageSize>();
if (sourcePageSize is not null)
{
if (currentPara.ParagraphProperties?.GetFirstChild<SectionProperties>() is null)
{
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;
}
@@ -630,7 +659,7 @@ internal static class MultiReplaceExt
var breakRun = new Run(new Break { Type = BreakValues.Page });
remainderPara.InsertAt(breakRun, 0);
bool addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(remainderPara, pendingOrientation.Value, addPageSize, sourceSection, portraitSection);
AddSectionProperties(remainderPara, pendingOrientation.Value, addPageSize, followingSection, portraitSection);
pendingOrientation = null;
}
else if (sectionChangeInsideGroup && lastOrientation.HasValue)
@@ -638,16 +667,16 @@ internal static class MultiReplaceExt
var breakRun = new Run(new Break { Type = BreakValues.Page });
remainderPara.InsertAt(breakRun, 0);
bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(remainderPara, lastOrientation.Value, addPageSize, sourceSection, portraitSection);
AddSectionProperties(remainderPara, lastOrientation.Value, addPageSize, followingSection, portraitSection);
}
else
{
// Если нет смены, но есть sourceSection с PageSize, копируем её в остаток
var sourcePageSize = sourceSection.GetFirstChild<PageSize>();
// Если нет смены, но есть followingSection с PageSize, копируем её в остаток
var sourcePageSize = followingSection.GetFirstChild<PageSize>();
if (sourcePageSize is not null)
{
remainderPara.ParagraphProperties ??= new ParagraphProperties();
remainderPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
remainderPara.ParagraphProperties.AppendChild(followingSection.CloneNode(true));
}
}
resultParas.Add(remainderPara);
@@ -679,7 +708,7 @@ internal static class MultiReplaceExt
Paragraph original,
ParagraphStructure structure,
int position,
SectionProperties sourceSection,
SectionProperties followingSection,
SectionProperties portraitSection,
List<Paragraph> resultParas)
{
@@ -690,7 +719,7 @@ internal static class MultiReplaceExt
currentPara = newPara;
bool addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(currentPara, pendingOrientation.Value, addPageSize, sourceSection, portraitSection);
AddSectionProperties(currentPara, pendingOrientation.Value, addPageSize, followingSection, portraitSection);
lastOrientation = pendingOrientation;
sectionChangeInsideGroup = true;
pendingOrientation = null;
@@ -738,7 +767,7 @@ internal static class MultiReplaceExt
ref bool emptyParagraphCreated,
bool hasEmptyParagraphsBefore,
Paragraph original,
SectionProperties sourceSection,
SectionProperties followingSection,
SectionProperties portraitSection,
List<Paragraph> resultParas)
{
@@ -764,7 +793,7 @@ internal static class MultiReplaceExt
var newPara = CloneParagraphWithoutSection(original);
resultParas.Add(newPara);
currentPara = newPara;
AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection);
AddSectionProperties(currentPara, breakType, false, followingSection, portraitSection);
lastOrientation = breakType;
sectionChangeInsideGroup = true;
firstSplitMarkerHandled = true;
@@ -788,14 +817,14 @@ internal static class MultiReplaceExt
{
lastTextPara.ParagraphProperties ??= new ParagraphProperties();
var sourcePageSize = sourceSection.GetFirstChild<PageSize>();
var sourcePageSize = followingSection.GetFirstChild<PageSize>();
if (sourcePageSize is not null)
{
lastTextPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
lastTextPara.ParagraphProperties.AppendChild(followingSection.CloneNode(true));
}
else
{
AddSectionProperties(lastTextPara, breakType, false, sourceSection, portraitSection);
AddSectionProperties(lastTextPara, breakType, false, followingSection, portraitSection);
}
lastOrientation = breakType;
sectionChangeInsideGroup = true;
@@ -805,7 +834,7 @@ internal static class MultiReplaceExt
}
else
{
AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection);
AddSectionProperties(currentPara, breakType, false, followingSection, portraitSection);
lastOrientation = breakType;
sectionChangeInsideGroup = true;
firstSplitMarkerHandled = true;