Как удалить страницу или лишний лист в документе MS Word при формировании из 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При формировании печатных форм часто возникает задача подготовить единый многостраничный документ MS Word для нескольких клиентов сразу. Например, нам нужно распечатать однотипные договоры или акты, где каждый клиент должен располагаться строго на отдельной странице. При этом максимальное число клиентов в документе ограничено (например, семью), а в реальности их может оказаться меньше — всего два или три. В таком случае перед нами встает техническая задача: как программно и корректно удалить оставшиеся пустые страницы из итогового файла MS Word средствами 1С?

В этой статье мы подробно разберем, как решать эту задачу с использованием COM-соединения (ActiveX/OLE), проанализируем частые ошибки (когда текст удаляется, а пустые листы остаются) и рассмотрим несколько профессиональных подходов — от простых закладок до динамической сборки документов «снизу вверх».

---

Причина проблемы: почему остаются пустые листы?

Когда мы пытаемся удалить текст страницы в MS Word из 1С, мы часто сталкиваемся с тем, что сам текст исчезает, но на его месте образуется пустая страница. Это происходит потому, что в документе остаются невидимые управляющие символы: разрывы страниц (Page Breaks) или пустые знаки абзацев. Чтобы страница удалилась полностью, нам необходимо захватить и удалить не только сам текст, но и разделитель страниц, который следует сразу за ним.

Проанализируем основные способы автоматизации этого процесса.

---

Решение 1. Удаление областей с помощью именованных закладок (Bookmarks)

Это классический и наиболее наглядный метод. В шаблоне MS Word мы заранее размечаем области для каждого возможного клиента и создаем именованные закладки (например, Область1, Область2, ..., Область7).

Важное правило позиционирования закладок: при создании шаблона в Word необходимо выделить весь текст страницы, включая разрыв страницы (Page Break) в самом конце листа, и только после этого вешать закладку. Если этого не сделать, текст удалится, а разрыв страницы останется, сформировав пустой лист.

Рассмотрим код на стороне 1С, реализующий удаление ненужных областей в обратном порядке (с конца документа):


// Определяем количество фактически выводимых покупателей
КоличествоПокупателей = МассивКлиентов.Количество(); 
МаксимумПокупателей   = 7;

// Создаем COM-объект для работы с Word
WordApp = Новый COMОбъект("Word.Application");
WordDoc = WordApp.Documents.Open(ПутьКШаблону);

// Начинаем удаление лишних страниц с конца документа
Пока МаксимумПокупателей > КоличествоПокупателей Цикл
    ИмяЗакладки = "Область" + МаксимумПокупателей;
    
    Если WordDoc.Bookmarks.Exists(ИмяЗакладки) Тогда
        // Выделяем область закладки через Selection
        Область = WordDoc.Application.Selection;
        Область.Start = WordDoc.Bookmarks(ИмяЗакладки).Start;
        Область.End   = WordDoc.Bookmarks(ИмяЗакладки).End;
        
        // Удаляем содержимое вместе с разрывом страницы
        Область.Delete();
    КонецЕсли;
    
    МаксимумПокупателей = МаксимумПокупателей - 1;
КонецЦикла;

Почему мы удаляем с конца? Если начать удаление с начала документа (например, со 2-й страницы), то координаты и индексы последующих закладок сместятся, что приведет к ошибкам позиционирования или удалению не тех данных. Метод «с конца» гарантирует стабильность структуры.

---

Решение 2. Оптимизация работы через объект Range (без использования Selection)

Использование объекта Selection заставляет MS Word физически перемещать курсор на экране компьютера. Это приводит к двум проблемам: во-первых, интерфейс начинает мерцать (что замедляет работу), во-вторых, если пользователь решит свернуть Word или переключиться на другое окно, фокус выделения может сбиться.

Для оптимизации скорости и надежности мы рекомендуем использовать объект Range. Все операции в этом случае происходят в оперативной памяти компьютера без визуального перемещения курсора.

Если в шаблоне разрыв страницы случайно не вошел в область закладки, мы можем программно расширить границы объекта Range на один символ вперед, чтобы принудительно захватить разделитель страниц:


МаксимумПокупателей = 7;

Пока МаксимумПокупателей > КоличествоПокупателей Цикл
    ИмяЗакладки = "Область" + МаксимумПокупателей;
    
    Если WordDoc.Bookmarks.Exists(ИмяЗакладки) Тогда
        // Работаем напрямую с объектом Range без Selection
        ОбластьRange = WordDoc.Bookmarks(ИмяЗакладки).Range;
        
        // Сдвигаем правую границу на 1 символ вперед, чтобы захватить разрыв страницы
        ОбластьRange.End = ОбластьRange.End + 1;
        
        // Удаляем весь диапазон в памяти
        ОбластьRange.Delete();
    КонецЕсли;
    
    МаксимумПокупателей = МаксимумПокупателей - 1;
КонецЦикла;

Такой подход работает в 2–3 раза быстрее и полностью исключает мерцание экрана при формировании печатной формы.

---

Решение 3. Удаление страниц по их абсолютному номеру (без ручных закладок)

Что делать, если в шаблоне нет возможности расставить закладки заранее? Мы можем использовать встроенную системную закладку Word с предопределенным именем \page. Она всегда ссылается на текущую активную страницу, на которой расположен курсор.

Выясним, как удалить, например, все страницы, начиная с 5-й по 7-ю:


WordApp = Новый COMОбъект("Word.Application");
WordDoc = WordApp.Documents.Open(ПутьКШаблону);
Selection = WordApp.Selection;

// Удаляем страницы в обратном порядке
Для НомерСтраницы = 7 По 5 Шаг -1 Цикл
    // wdGoToPage = 1, wdGoToAbsolute = 1
    // Перемещаем курсор на начало удаляемой страницы
    Selection.GoTo(1, 1, НомерСтраницы);
    
    // Выбираем системную закладку "\page", которая выделяет всю текущую страницу
    Selection.Bookmarks("\page").Range.Delete();
КонецЦикла;

Этот способ универсален, так как не требует предварительной подготовки макета и разметки закладок вручную.

---

Решение 4. Использование разделов документа (Sections)

Для сложных документов со смешанной ориентацией страниц (например, когда первый лист книжный, а второй — альбомный с широкой таблицей) лучшим решением будет разбиение шаблона на Разделы (Sections) вместо обычных страниц. Разрыв раздела создается в Word через меню «Макет -> Разрывы -> Следующая страница».

Преимущество разделов в том, что при удалении раздела через OLE автоматически удаляются все его свойства, разметки колонок, поля и сам разделитель. Пустые листы гарантированно не останутся.

Рассмотрим пример удаления лишних разделов:


// Удаляем лишние разделы, начиная с конца документа
Для НомерРаздела = 7 По 5 Шаг -1 Цикл
    Если WordDoc.Sections.Count >= НомерРаздела Тогда
        // Удаляем диапазон конкретного раздела
        WordDoc.Sections.Item(НомерРаздела).Range.Delete();
    КонецЕсли;
КонецЦикла;

---

Решение 5. Очистка документа от «мусорных» разрывов страниц

Если после выполнения всех алгоритмов в самом конце документа все же остался один пустой лист, это признак наличия «зависшего» символа разрыва страницы (в кодировке Word он обозначается как ^m) или лишних пустых абзацев (^p). Мы можем запустить процедуру автоматического поиска и замены в самом конце формирования документа:


// Настраиваем объект поиска и замены
ПоискИЗамена = WordDoc.Content.Find;
ПоискИЗамена.ClearFormatting();
ПоискИЗамена.Replacement.ClearFormatting();

// Ищем два идущих подряд разрыва страницы и заменяем их на один
ПоискИЗамена.Execute("^m^m", Ложь, Истина, Ложь, Ложь, Ложь, Истина, 1, Ложь, "^m", 2); // 2 = wdReplaceAll (Заменить все)

// Если разрыв остался на самом конце документа перед знаком абзаца
ПоискИЗамена.Execute("^m^p", Ложь, Истина, Ложь, Ложь, Ложь, Истина, 1, Ложь, "", 2);

---

Альтернативный подход: Динамическое построение документа «снизу вверх»

Удаление страниц — это всегда ресурсоемкая операция для движка MS Word. Программа тратит время на пересчет верстки, изменение нумерации страниц и перераспределение абзацев.

Опытные разработчики советуют отказаться от стратегии «сделать максимум страниц и удалить лишнее» в пользу стратегии «создать пустой документ и динамически добавлять страницы по мере необходимости».

Проанализируем этот алгоритм:

  1. Создаем шаблон Word, содержащий ровно одну страницу (каркас для одного клиента).
  2. В коде 1С копируем содержимое этого шаблона в буфер обмена.
  3. В цикле по реальным клиентам вставляем шаблон в итоговый документ.
  4. Перед каждой новой вставкой (начиная со второго клиента) программно добавляем разрыв страницы.
  5. Заполняем параметры на лету.

Посмотрим на практический пример реализации этого метода:


WordApp = Новый COMОбъект("Word.Application");
WordApp.Visible = Ложь;

// Открываем шаблон с одним листом
ШаблонDoc = WordApp.Documents.Open(ПутьКШаблону);
// Создаем новый чистый документ для результата
ИтоговыйDoc = WordApp.Documents.Add();

// Копируем весь шаблон в буфер обмена
ШаблонDoc.Range().Copy();

КоличествоКлиентов = МассивКлиентов.Количество();

Для Индекс = 1 По КоличествоКлиентов Цикл
    Если Индекс > 1 Тогда
        // Переходим в конец итогового документа
        WordApp.Selection.EndKey(6); // wdStory = 6
        // Вставляем разрыв страницы
        WordApp.Selection.InsertBreak(7); // wdPageBreak = 7
    КонецЕсли;
    
    // Вставляем скопированный лист из буфера
    WordApp.Selection.EndKey(6);
    WordApp.Selection.Paste();
    
    // Производим замену параметров для текущего клиента
    Поиск = WordApp.Selection.Find;
    Поиск.ClearFormatting();
    Поиск.Replacement.ClearFormatting();
    
    ДанныеКлиента = МассивКлиентов[Индекс - 1];
    Поиск.Execute("[ФИО_Клиента]", Ложь, Истина, Ложь, Ложь, Ложь, Истина, 1, Ложь, ДанныеКлиента.ФИО, 2);
    Поиск.Execute("[НомерДоговора]", Ложь, Истина, Ложь, Ложь, Ложь, Истина, 1, Ложь, ДанныеКлиента.Номер, 2);
КонецЦикла;

// Закрываем шаблон без сохранения изменений
ШаблонDoc.Close(Ложь);

// Сохраняем итоговый файл
ИтоговыйDoc.SaveAs(ПутьКВыгрузке);
WordApp.Quit();

Преимущества динамического подхода:

Выбор конкретного метода зависит от архитектуры вашей печатной формы. Если документ имеет крайне сложную верстку, которую трудно переносить через буфер обмена, используйте метод с Range-закладками (Решение 2). Во всех остальных случаях динамическое построение документа «снизу вверх» будет являться наиболее профессиональным и отказоустойчивым решением.

← На главную