From 7fec0ea1bf2a5ecd9ebd64b0df7b7821bf4633a1 Mon Sep 17 00:00:00 2001 From: melekhin Date: Tue, 1 Sep 2026 13:45:51 +0700 Subject: [PATCH] 777 --- QWERTYkez.WordProcessor/MultiReplace.cs | 101 +++++++++++++++--------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/QWERTYkez.WordProcessor/MultiReplace.cs b/QWERTYkez.WordProcessor/MultiReplace.cs index 8b1d439..c1355fe 100644 --- a/QWERTYkez.WordProcessor/MultiReplace.cs +++ b/QWERTYkez.WordProcessor/MultiReplace.cs @@ -180,7 +180,6 @@ internal static class MultiReplaceExt return sb.ToString(); } - /// Клонирует параграф, удаляя все SectionProperties и PageBreakBefore. 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()) - { - var ps = bodySec.GetFirstChild(); - if (ps is not null && ps.Orient?.Value == PageOrientationValues.Landscape) - return; - } + // Удаляем все существующие секции из Body + var existingSections = body.Elements().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 // ---------- ОСНОВНОЙ АЛГОРИТМ ---------- - /// Основной алгоритм множественной замены с поддержкой разрывов и смены ориентации. private static List? ProcessMultiReplacements( Paragraph original, IEnumerable>>? stringReplacements, @@ -424,6 +421,22 @@ internal static class MultiReplaceExt prevPara = prevPara.PreviousSibling(); } + // Определяем секцию, которая следует за original (если у original нет секции) + SectionProperties? followingSection = null; + if (original.ParagraphProperties?.GetFirstChild()?.CloneNode(true) is not SectionProperties sourceSection) + { + var nextPara = original.NextSibling(); + if (nextPara is not null) + { + followingSection = nextPara.ParagraphProperties?.GetFirstChild()?.CloneNode(true) as SectionProperties; + } + } + else + { + followingSection = sourceSection; + } + followingSection ??= new SectionProperties(); + // 1. Сбор определений var definitions = new List(); 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()?.CloneNode(true) as SectionProperties; - if (sourceSection is null && body is not null) - { - sourceSection = body.Elements().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().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() 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(); + var sourcePageSize = followingSection.GetFirstChild(); if (sourcePageSize is not null) { if (currentPara.ParagraphProperties?.GetFirstChild() 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(); + // Если нет смены, но есть followingSection с PageSize, копируем её в остаток + var sourcePageSize = followingSection.GetFirstChild(); 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 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 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(); + var sourcePageSize = followingSection.GetFirstChild(); 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;