Как заполнить колонтитулы в документе MS Word из 1С:Предприятие?

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

При автоматизации документооборота в 1С программисты часто сталкиваются с задачей формирования печатных форм на основе шаблонов Microsoft Word (файлы формата .doc или .docx). Заполнение обычных параметров в тексте документа, таких как ФИО контрагента, дата или сумма договора, обычно не вызывает трудностей и реализуется через стандартный поиск и замену. Однако, когда дело доходит до заполнения параметров, расположенных в верхних или нижних колонтитулах (например, номера договора на каждой странице или реквизитов компании внизу страницы), стандартный код замены перестает работать. Параметры в колонтитулах попросту остаются незаполненными фигурные скобки и маркеры так и остаются в тексте.

В рамках этой статьи мы подробно разберем, почему возникает эта проблема, проанализируем внутреннее устройство объектной модели MS Word и рассмотрим несколько эффективных способов решения: от простого точечного исправления до создания универсального и быстрого алгоритма заполнения любых областей шаблона.

Выясняем причину: почему стандартный поиск не видит колонтитулы?

Для начала давайте разберем типичную ошибку. Большинство разработчиков используют метод поиска и замены в основном теле документа с помощью обращения к свойству Content:


Замена = Документ.Content.Find;
Замена.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);

Этот код отлично работает для основного текста, но совершенно не затрагивает колонтитулы. Дело в том, что в объектной модели MS Word области документа строго разделены. Колонтитулы не входят в основной диапазон Content. Они хранятся в отдельных коллекциях Headers (верхние колонтитулы) и Footers (нижние колонтитулы) для каждого раздела документа Sections.

Попытка обратиться напрямую к первому элементу коллекции колонтитулов также часто заканчивается неудачей:


мКолонтитул = Документ.Sections.Item(1).Footers.Item(1).Range;
мКолонтитул.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);

Выполняя этот код, разработчик видит, что программа отрабатывает без ошибок, но замена в файле все равно не происходит. Почему так получается?

Всё дело в настройках разметки самого шаблона Word. В параметрах страницы документа может быть установлена галочка «Особый колонтитул для первой страницы». В объектной модели Word коллекции Headers и Footers адресуются по встроенному системному перечислению WdHeaderFooterIndex, которое имеет три числовых значения:

  1. 1 (wdHeaderFooterPrimary) — основной колонтитул. Он отображается на всех страницах документа по умолчанию.
  2. 2 (wdHeaderFooterFirstPage) — колонтитул первой страницы.
  3. 3 (wdHeaderFooterEvenPages) — колонтитул четных страниц (используется при включенной опции разделения четных и нечетных страниц для двухсторонней печати).

Если в шаблоне включен особый колонтитул первой страницы, то видимым на первой странице будет именно колонтитул с индексом 2. Пытаясь записать данные в Item(1), мы производим замену в основном (скрытом на первой странице) колонтитуле. Физически текст меняется, но на экране и при печати первой страницы мы этого не видим.

Решение №1: Точечная замена с использованием правильного индекса

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


// Обращаемся к нижнему колонтитулу первой страницы (индекс 2)
мКолонтитул = Документ.Sections.Item(1).Footers.Item(2).Range;
мКолонтитул.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);

Этот способ мгновенно решает проблему для простых одностраничных документов или шаблонов с жестко заданной структурой. Однако он имеет существенный недостаток: если документ станет многостраничным, на последующих страницах замена в нижнем колонтитуле не произойдет, так как там начнет отображаться основной колонтитул с индексом 1.

Решение №2: Универсальный обход всех разделов и колонтитулов в цикле

Чтобы сделать наш алгоритм устойчивым к любым изменениям структуры шаблона (наличию нескольких разделов, разделению на четные/нечетные страницы или особым колонтитулам первой страницы), мы должны применить циклический обход всех возможных областей колонтитулов. Проанализируем следующий программный код:


// Перебираем все разделы документа
Для Каждого Раздел Из Документ.Sections Цикл
    
    // Обходим все типы верхних колонтитулов (их всегда ровно 3 в каждом разделе)
    Для Каждого Колонтитул Из Раздел.Headers Цикл
        Колонтитул.Range.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
    КонецЦикла;
    
    // Обходим все типы нижних колонтитулов
    Для Каждого Колонтитул Из Раздел.Footers Цикл
        Колонтитул.Range.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
    КонецЦикла;
    
КонецЦикла;

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

Решение №3: Глобальный поиск через StoryRanges (наиболее надежный COM-метод)

Помимо колонтитулов и основного текста, параметры автозаполнения в шаблонах Word могут находиться в сносках, комментариях или графических надписях (объектах TextBox/Текстовое поле). Чтобы не писать отдельные циклы для каждого типа объектов, мы можем использовать встроенный механизм объектной модели MS Word — коллекцию областей документа StoryRanges.

Каждая область (основной текст, колонтитулы, сноски) представляет собой отдельный элемент в этой коллекции. Давайте рассмотрим профессиональный метод поиска и замены, который гарантированно обработает абсолютно все скрытые уголки нашего файла:


// Перебираем все базовые области документа
Для Каждого Область Из Документ.StoryRanges Цикл
    
    // Выполняем замену в текущей области
    Область.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
    
    // Обрабатываем связанные области (например, цепочки текстовых блоков)
    ТекущаяОбласть = Область.NextStoryRange;
    Пока ТекущаяОбласть <> Неопределено Цикл
        ТекущаяОбласть.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
        ТекущаяОбласть = ТекущаяОбласть.NextStoryRange;
    КонецЦикла;
    
КонецЦикла;

Этот алгоритм является промышленным стандартом при работе с COM-объектом Word в 1С. Он избавляет от необходимости заботиться о структуре документа и обеспечивает 100% покрытие заменяемых параметров.

Рекомендация: Альтернатива без использования COM-объектов

Хотя технология COM (использование Новый COMОбъект("Word.Application")) решает задачу, она обладает критическими недостатками в современных реалиях:

Если вы разрабатываете современную конфигурацию, настоятельно рекомендуем обратить внимание на возможности **БСП (Библиотеки стандартных подсистем)**, а именно на модуль УправлениеПечатью. Этот механизм позволяет работать с файлами .docx напрямую как с ZIP-архивами. 1С на уровне сервера распаковывает документ, с помощью быстрых алгоритмов работы с XML модифицирует файлы разметки (включая файлы колонтитулов header1.xml, footer1.xml) и собирает архив обратно. Это работает в десятки раз быстрее, не требует установленного MS Word и полностью совместимо с серверами на Linux.

← На главную