666
This commit is contained in:
@@ -238,10 +238,6 @@ internal static class MultiReplaceExt
|
|||||||
para.AppendChild(textRun);
|
para.AppendChild(textRun);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Добавляет SectionProperties к параграфу. Все значения (PageSize, PageMargin) берутся из документа.
|
|
||||||
/// Для книжных секций (addPageSize=false) Orient не устанавливается (not set).
|
|
||||||
/// </summary>
|
|
||||||
private static void AddSectionProperties(
|
private static void AddSectionProperties(
|
||||||
Paragraph para,
|
Paragraph para,
|
||||||
BreakType splitValue,
|
BreakType splitValue,
|
||||||
@@ -406,6 +402,8 @@ internal static class MultiReplaceExt
|
|||||||
target.AppendChild(child.CloneNode(true));
|
target.AppendChild(child.CloneNode(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- ОСНОВНОЙ АЛГОРИТМ ----------
|
||||||
|
|
||||||
/// <summary>Основной алгоритм множественной замены с поддержкой разрывов и смены ориентации.</summary>
|
/// <summary>Основной алгоритм множественной замены с поддержкой разрывов и смены ориентации.</summary>
|
||||||
private static List<Paragraph>? ProcessMultiReplacements(
|
private static List<Paragraph>? ProcessMultiReplacements(
|
||||||
Paragraph original,
|
Paragraph original,
|
||||||
@@ -501,7 +499,7 @@ internal static class MultiReplaceExt
|
|||||||
bool firstSplitMarkerHandled = false;
|
bool firstSplitMarkerHandled = false;
|
||||||
int textCount = 0;
|
int textCount = 0;
|
||||||
Paragraph? lastTextPara = null;
|
Paragraph? lastTextPara = null;
|
||||||
bool emptyParagraphCreated = false; // был ли создан пустой параграф для not set
|
bool emptyParagraphCreated = false;
|
||||||
|
|
||||||
for (int i = 0; i < matches.Count; i++)
|
for (int i = 0; i < matches.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -553,145 +551,40 @@ internal static class MultiReplaceExt
|
|||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(item.Text))
|
if (!string.IsNullOrEmpty(item.Text))
|
||||||
{
|
{
|
||||||
// Если есть отложенная ориентация — применяем её
|
ProcessTextItem(
|
||||||
if (pendingOrientation.HasValue)
|
item.Text,
|
||||||
{
|
ref pendingOrientation,
|
||||||
var newPara = CloneParagraphWithoutSection(original);
|
ref currentPara,
|
||||||
resultParas.Add(newPara);
|
ref lastTextPara,
|
||||||
currentPara = newPara;
|
ref textCount,
|
||||||
|
ref pageBreakBeforeNext,
|
||||||
// Определяем addPageSize:
|
ref sectionChangeInsideGroup,
|
||||||
// - если был создан пустой параграф для not set (emptyParagraphCreated == true), то первый текст получает addPageSize = true для landscape, false для portrait
|
ref lastOrientation,
|
||||||
// - иначе addPageSize = (pendingOrientation == BreakType.NewLandscapeSection)
|
original,
|
||||||
bool addPageSize;
|
structure,
|
||||||
if (emptyParagraphCreated)
|
match.Start,
|
||||||
{
|
sourceSection,
|
||||||
addPageSize = (pendingOrientation.Value == BreakType.NewLandscapeSection);
|
portraitSection,
|
||||||
}
|
resultParas);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (item.BreakValue.HasValue)
|
else if (item.BreakValue.HasValue)
|
||||||
{
|
{
|
||||||
var breakType = item.BreakValue.Value;
|
ProcessBreakItem(
|
||||||
if (breakType == BreakType.PageBreak)
|
item.BreakValue.Value,
|
||||||
{
|
ref pendingOrientation,
|
||||||
if (currentPara is not null)
|
ref currentPara,
|
||||||
{
|
ref lastTextPara,
|
||||||
var breakRun = new Run(new Break { Type = BreakValues.Page });
|
ref pageBreakBeforeNext,
|
||||||
var lastRun = currentPara.Descendants<Run>().LastOrDefault();
|
ref firstSplitMarkerHandled,
|
||||||
if (lastRun?.RunProperties is not null)
|
ref sectionChangeInsideGroup,
|
||||||
breakRun.RunProperties = (RunProperties)lastRun.RunProperties.CloneNode(true);
|
ref lastOrientation,
|
||||||
currentPara.AppendChild(breakRun);
|
ref textCount,
|
||||||
}
|
ref emptyParagraphCreated,
|
||||||
else
|
hasEmptyParagraphsBefore,
|
||||||
{
|
original,
|
||||||
pageBreakBeforeNext = true;
|
sourceSection,
|
||||||
}
|
portraitSection,
|
||||||
}
|
resultParas);
|
||||||
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<SectionProperties>() is null)
|
|
||||||
{
|
|
||||||
lastTextPara.ParagraphProperties ??= new ParagraphProperties();
|
|
||||||
|
|
||||||
var sourcePageSize = sourceSection.GetFirstChild<PageSize>();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,6 +598,21 @@ internal static class MultiReplaceExt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Если внутри группы не было смены, но есть sourceSection с PageSize,
|
||||||
|
// копируем её в последний параграф группы (если он не имеет секции)
|
||||||
|
if (!sectionChangeInsideGroup && currentPara is not null)
|
||||||
|
{
|
||||||
|
var sourcePageSize = sourceSection.GetFirstChild<PageSize>();
|
||||||
|
if (sourcePageSize is not null)
|
||||||
|
{
|
||||||
|
if (currentPara.ParagraphProperties?.GetFirstChild<SectionProperties>() is null)
|
||||||
|
{
|
||||||
|
currentPara.ParagraphProperties ??= new ParagraphProperties();
|
||||||
|
currentPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EnsureBodyLandscapeSection(body!, sourceSection, sectionChangeInsideGroup, lastOrientation);
|
EnsureBodyLandscapeSection(body!, sourceSection, sectionChangeInsideGroup, lastOrientation);
|
||||||
|
|
||||||
currentPos = match.End;
|
currentPos = match.End;
|
||||||
@@ -716,6 +624,7 @@ internal static class MultiReplaceExt
|
|||||||
var remainderPara = BuildRemainderParagraph(original, structure, currentPos);
|
var remainderPara = BuildRemainderParagraph(original, structure, currentPos);
|
||||||
if (remainderPara is not null)
|
if (remainderPara is not null)
|
||||||
{
|
{
|
||||||
|
// Если есть отложенная ориентация — применяем её
|
||||||
if (pendingOrientation.HasValue)
|
if (pendingOrientation.HasValue)
|
||||||
{
|
{
|
||||||
var breakRun = new Run(new Break { Type = BreakValues.Page });
|
var breakRun = new Run(new Break { Type = BreakValues.Page });
|
||||||
@@ -731,6 +640,16 @@ internal static class MultiReplaceExt
|
|||||||
bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection);
|
bool addPageSize = (lastOrientation.Value == BreakType.NewLandscapeSection);
|
||||||
AddSectionProperties(remainderPara, lastOrientation.Value, addPageSize, sourceSection, portraitSection);
|
AddSectionProperties(remainderPara, lastOrientation.Value, addPageSize, sourceSection, portraitSection);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Если нет смены, но есть sourceSection с PageSize, копируем её в остаток
|
||||||
|
var sourcePageSize = sourceSection.GetFirstChild<PageSize>();
|
||||||
|
if (sourcePageSize is not null)
|
||||||
|
{
|
||||||
|
remainderPara.ParagraphProperties ??= new ParagraphProperties();
|
||||||
|
remainderPara.ParagraphProperties.AppendChild(sourceSection.CloneNode(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
resultParas.Add(remainderPara);
|
resultParas.Add(remainderPara);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -745,4 +664,159 @@ internal static class MultiReplaceExt
|
|||||||
|
|
||||||
return resultParas.Count > 0 ? resultParas : null;
|
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<Paragraph> 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<Paragraph> resultParas)
|
||||||
|
{
|
||||||
|
if (breakType == BreakType.PageBreak)
|
||||||
|
{
|
||||||
|
if (currentPara is not null)
|
||||||
|
{
|
||||||
|
var breakRun = new Run(new Break { Type = BreakValues.Page });
|
||||||
|
var lastRun = currentPara.Descendants<Run>().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<SectionProperties>() is null)
|
||||||
|
{
|
||||||
|
lastTextPara.ParagraphProperties ??= new ParagraphProperties();
|
||||||
|
|
||||||
|
var sourcePageSize = sourceSection.GetFirstChild<PageSize>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user