diff --git a/QWERTYkez.WordProcessor/MultiReplace.cs b/QWERTYkez.WordProcessor/MultiReplace.cs
index 496fdd2..8b1d439 100644
--- a/QWERTYkez.WordProcessor/MultiReplace.cs
+++ b/QWERTYkez.WordProcessor/MultiReplace.cs
@@ -238,10 +238,6 @@ internal static class MultiReplaceExt
para.AppendChild(textRun);
}
- ///
- /// Добавляет SectionProperties к параграфу. Все значения (PageSize, PageMargin) берутся из документа.
- /// Для книжных секций (addPageSize=false) Orient не устанавливается (not set).
- ///
private static void AddSectionProperties(
Paragraph para,
BreakType splitValue,
@@ -406,6 +402,8 @@ internal static class MultiReplaceExt
target.AppendChild(child.CloneNode(true));
}
+ // ---------- ОСНОВНОЙ АЛГОРИТМ ----------
+
/// Основной алгоритм множественной замены с поддержкой разрывов и смены ориентации.
private static List? ProcessMultiReplacements(
Paragraph original,
@@ -501,7 +499,7 @@ internal static class MultiReplaceExt
bool firstSplitMarkerHandled = false;
int textCount = 0;
Paragraph? lastTextPara = null;
- bool emptyParagraphCreated = false; // был ли создан пустой параграф для not set
+ bool emptyParagraphCreated = false;
for (int i = 0; i < matches.Count; i++)
{
@@ -553,145 +551,40 @@ internal static class MultiReplaceExt
{
if (!string.IsNullOrEmpty(item.Text))
{
- // Если есть отложенная ориентация — применяем её
- if (pendingOrientation.HasValue)
- {
- var newPara = CloneParagraphWithoutSection(original);
- resultParas.Add(newPara);
- currentPara = newPara;
-
- // Определяем addPageSize:
- // - если был создан пустой параграф для not set (emptyParagraphCreated == true), то первый текст получает addPageSize = true для landscape, false для portrait
- // - иначе addPageSize = (pendingOrientation == BreakType.NewLandscapeSection)
- bool addPageSize;
- if (emptyParagraphCreated)
- {
- addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
- }
- else
- {
- addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
- }
- AddSectionProperties(currentPara, pendingOrientation.Value, addPageSize, sourceSection, portraitSection);
- lastOrientation = pendingOrientation;
- sectionChangeInsideGroup = true;
- pendingOrientation = null;
- textCount++;
- lastTextPara = currentPara;
-
- if (pageBreakBeforeNext)
- {
- var breakRun = new Run(new Break { Type = BreakValues.Page });
- currentPara.InsertAt(breakRun, 0);
- pageBreakBeforeNext = false;
- }
-
- InsertFormattedRun(currentPara, item.Text, structure, match.Start);
- }
- else
- {
- var newPara = CloneParagraphWithoutSection(original);
- resultParas.Add(newPara);
- currentPara = newPara;
- textCount++;
- lastTextPara = currentPara;
-
- if (pageBreakBeforeNext)
- {
- var breakRun = new Run(new Break { Type = BreakValues.Page });
- currentPara.InsertAt(breakRun, 0);
- pageBreakBeforeNext = false;
- }
-
- InsertFormattedRun(currentPara, item.Text, structure, match.Start);
- }
+ ProcessTextItem(
+ item.Text,
+ ref pendingOrientation,
+ ref currentPara,
+ ref lastTextPara,
+ ref textCount,
+ ref pageBreakBeforeNext,
+ ref sectionChangeInsideGroup,
+ ref lastOrientation,
+ original,
+ structure,
+ match.Start,
+ sourceSection,
+ portraitSection,
+ resultParas);
}
else if (item.BreakValue.HasValue)
{
- var breakType = item.BreakValue.Value;
- if (breakType == BreakType.PageBreak)
- {
- if (currentPara is not null)
- {
- var breakRun = new Run(new Break { Type = BreakValues.Page });
- var lastRun = currentPara.Descendants().LastOrDefault();
- if (lastRun?.RunProperties is not null)
- breakRun.RunProperties = (RunProperties)lastRun.RunProperties.CloneNode(true);
- currentPara.AppendChild(breakRun);
- }
- else
- {
- pageBreakBeforeNext = true;
- }
- }
- else if (breakType == BreakType.NewLandscapeSection || breakType == BreakType.NewPortraitSection)
- {
- // Если это первый маркер в группе и перед original есть пустые параграфы,
- // создаём пустой параграф с not set
- if (currentPara is null && hasEmptyParagraphsBefore && !firstSplitMarkerHandled)
- {
- var newPara = CloneParagraphWithoutSection(original);
- resultParas.Add(newPara);
- currentPara = newPara;
- // Добавляем секцию not set (без PageSize)
- AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection);
- lastOrientation = breakType;
- sectionChangeInsideGroup = true;
- firstSplitMarkerHandled = true;
- emptyParagraphCreated = true;
- pendingOrientation = null;
- textCount = 0;
- lastTextPara = null;
- }
- else if (currentPara is null && !hasEmptyParagraphsBefore && !firstSplitMarkerHandled)
- {
- // Нет пустых параграфов, нет текущего параграфа — запоминаем ориентацию для первого текста
- pendingOrientation = breakType;
- firstSplitMarkerHandled = true;
- // emptyParagraphCreated остаётся false
- }
- else if (currentPara is not null)
- {
- if (!firstSplitMarkerHandled)
- {
- // Это первый маркер смены в группе (после текста)
- if (lastTextPara is not null)
- {
- if (lastTextPara.ParagraphProperties?.GetFirstChild() is null)
- {
- lastTextPara.ParagraphProperties ??= new ParagraphProperties();
-
- var sourcePageSize = sourceSection.GetFirstChild();
- if (sourcePageSize is not null)
- {
- lastTextPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
- }
- else
- {
- AddSectionProperties(lastTextPara, breakType, false, sourceSection, portraitSection);
- }
- lastOrientation = breakType;
- sectionChangeInsideGroup = true;
- }
- firstSplitMarkerHandled = true;
- pendingOrientation = null;
- }
- else
- {
- AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection);
- lastOrientation = breakType;
- sectionChangeInsideGroup = true;
- firstSplitMarkerHandled = true;
- pendingOrientation = null;
- }
- }
- else
- {
- // Это не первый маркер — устанавливаем отложенную ориентацию для следующего текста
- pendingOrientation = breakType;
- }
- }
- }
+ ProcessBreakItem(
+ item.BreakValue.Value,
+ ref pendingOrientation,
+ ref currentPara,
+ ref lastTextPara,
+ ref pageBreakBeforeNext,
+ ref firstSplitMarkerHandled,
+ ref sectionChangeInsideGroup,
+ ref lastOrientation,
+ ref textCount,
+ ref emptyParagraphCreated,
+ hasEmptyParagraphsBefore,
+ original,
+ sourceSection,
+ portraitSection,
+ resultParas);
}
}
@@ -705,6 +598,21 @@ internal static class MultiReplaceExt
}
}
+ // Если внутри группы не было смены, но есть sourceSection с PageSize,
+ // копируем её в последний параграф группы (если он не имеет секции)
+ if (!sectionChangeInsideGroup && currentPara is not null)
+ {
+ var sourcePageSize = sourceSection.GetFirstChild();
+ if (sourcePageSize is not null)
+ {
+ if (currentPara.ParagraphProperties?.GetFirstChild() is null)
+ {
+ currentPara.ParagraphProperties ??= new ParagraphProperties();
+ currentPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
+ }
+ }
+ }
+
EnsureBodyLandscapeSection(body!, sourceSection, sectionChangeInsideGroup, lastOrientation);
currentPos = match.End;
@@ -716,6 +624,7 @@ internal static class MultiReplaceExt
var remainderPara = BuildRemainderParagraph(original, structure, currentPos);
if (remainderPara is not null)
{
+ // Если есть отложенная ориентация — применяем её
if (pendingOrientation.HasValue)
{
var breakRun = new Run(new Break { Type = BreakValues.Page });
@@ -731,6 +640,16 @@ internal static class MultiReplaceExt
bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection);
AddSectionProperties(remainderPara, lastOrientation.Value, addPageSize, sourceSection, portraitSection);
}
+ else
+ {
+ // Если нет смены, но есть sourceSection с PageSize, копируем её в остаток
+ var sourcePageSize = sourceSection.GetFirstChild();
+ if (sourcePageSize is not null)
+ {
+ remainderPara.ParagraphProperties ??= new ParagraphProperties();
+ remainderPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
+ }
+ }
resultParas.Add(remainderPara);
}
}
@@ -745,4 +664,159 @@ internal static class MultiReplaceExt
return resultParas.Count > 0 ? resultParas : null;
}
+
+ // ---------- ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ ДЛЯ ОБРАБОТКИ ГРУПП ----------
+
+ private static void ProcessTextItem(
+ string text,
+ ref BreakType? pendingOrientation,
+ ref Paragraph? currentPara,
+ ref Paragraph? lastTextPara,
+ ref int textCount,
+ ref bool pageBreakBeforeNext,
+ ref bool sectionChangeInsideGroup,
+ ref BreakType? lastOrientation,
+ Paragraph original,
+ ParagraphStructure structure,
+ int position,
+ SectionProperties sourceSection,
+ SectionProperties portraitSection,
+ List resultParas)
+ {
+ if (pendingOrientation.HasValue)
+ {
+ var newPara = CloneParagraphWithoutSection(original);
+ resultParas.Add(newPara);
+ currentPara = newPara;
+
+ bool addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
+ AddSectionProperties(currentPara, pendingOrientation.Value, addPageSize, sourceSection, portraitSection);
+ lastOrientation = pendingOrientation;
+ sectionChangeInsideGroup = true;
+ pendingOrientation = null;
+ textCount++;
+ lastTextPara = currentPara;
+
+ if (pageBreakBeforeNext)
+ {
+ var breakRun = new Run(new Break { Type = BreakValues.Page });
+ currentPara.InsertAt(breakRun, 0);
+ pageBreakBeforeNext = false;
+ }
+
+ InsertFormattedRun(currentPara, text, structure, position);
+ }
+ else
+ {
+ var newPara = CloneParagraphWithoutSection(original);
+ resultParas.Add(newPara);
+ currentPara = newPara;
+ textCount++;
+ lastTextPara = currentPara;
+
+ if (pageBreakBeforeNext)
+ {
+ var breakRun = new Run(new Break { Type = BreakValues.Page });
+ currentPara.InsertAt(breakRun, 0);
+ pageBreakBeforeNext = false;
+ }
+
+ InsertFormattedRun(currentPara, text, structure, position);
+ }
+ }
+
+ private static void ProcessBreakItem(
+ BreakType breakType,
+ ref BreakType? pendingOrientation,
+ ref Paragraph? currentPara,
+ ref Paragraph? lastTextPara,
+ ref bool pageBreakBeforeNext,
+ ref bool firstSplitMarkerHandled,
+ ref bool sectionChangeInsideGroup,
+ ref BreakType? lastOrientation,
+ ref int textCount,
+ ref bool emptyParagraphCreated,
+ bool hasEmptyParagraphsBefore,
+ Paragraph original,
+ SectionProperties sourceSection,
+ SectionProperties portraitSection,
+ List resultParas)
+ {
+ if (breakType == BreakType.PageBreak)
+ {
+ if (currentPara is not null)
+ {
+ var breakRun = new Run(new Break { Type = BreakValues.Page });
+ var lastRun = currentPara.Descendants().LastOrDefault();
+ if (lastRun?.RunProperties is not null)
+ breakRun.RunProperties = (RunProperties)lastRun.RunProperties.CloneNode(true);
+ currentPara.AppendChild(breakRun);
+ }
+ else
+ {
+ pageBreakBeforeNext = true;
+ }
+ }
+ else if (breakType == BreakType.NewLandscapeSection || breakType == BreakType.NewPortraitSection)
+ {
+ if (currentPara is null && hasEmptyParagraphsBefore && !firstSplitMarkerHandled)
+ {
+ var newPara = CloneParagraphWithoutSection(original);
+ resultParas.Add(newPara);
+ currentPara = newPara;
+ AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection);
+ lastOrientation = breakType;
+ sectionChangeInsideGroup = true;
+ firstSplitMarkerHandled = true;
+ emptyParagraphCreated = true;
+ pendingOrientation = null;
+ textCount = 0;
+ lastTextPara = null;
+ }
+ else if (currentPara is null && !hasEmptyParagraphsBefore && !firstSplitMarkerHandled)
+ {
+ pendingOrientation = breakType;
+ firstSplitMarkerHandled = true;
+ }
+ else if (currentPara is not null)
+ {
+ if (!firstSplitMarkerHandled)
+ {
+ if (lastTextPara is not null)
+ {
+ if (lastTextPara.ParagraphProperties?.GetFirstChild() is null)
+ {
+ lastTextPara.ParagraphProperties ??= new ParagraphProperties();
+
+ var sourcePageSize = sourceSection.GetFirstChild();
+ if (sourcePageSize is not null)
+ {
+ lastTextPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
+ }
+ else
+ {
+ AddSectionProperties(lastTextPara, breakType, false, sourceSection, portraitSection);
+ }
+ lastOrientation = breakType;
+ sectionChangeInsideGroup = true;
+ }
+ firstSplitMarkerHandled = true;
+ pendingOrientation = null;
+ }
+ else
+ {
+ AddSectionProperties(currentPara, breakType, false, sourceSection, portraitSection);
+ lastOrientation = breakType;
+ sectionChangeInsideGroup = true;
+ firstSplitMarkerHandled = true;
+ pendingOrientation = null;
+ }
+ }
+ else
+ {
+ pendingOrientation = breakType;
+ }
+ }
+ }
+ }
}
\ No newline at end of file