При автоматизации документооборота в 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, которое имеет три числовых значения:
wdHeaderFooterPrimary) — основной колонтитул. Он отображается на всех страницах документа по умолчанию.wdHeaderFooterFirstPage) — колонтитул первой страницы.wdHeaderFooterEvenPages) — колонтитул четных страниц (используется при включенной опции разделения четных и нечетных страниц для двухсторонней печати).Если в шаблоне включен особый колонтитул первой страницы, то видимым на первой странице будет именно колонтитул с индексом 2. Пытаясь записать данные в Item(1), мы производим замену в основном (скрытом на первой странице) колонтитуле. Физически текст меняется, но на экране и при печати первой страницы мы этого не видим.
Если мы точно знаем структуру нашего шаблона и уверены, что замену нужно произвести только на первой странице, где включен особый колонтитул, мы можем явно указать индекс 2 при обращении к коллекции колонтитулов:
// Обращаемся к нижнему колонтитулу первой страницы (индекс 2)
мКолонтитул = Документ.Sections.Item(1).Footers.Item(2).Range;
мКолонтитул.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
Этот способ мгновенно решает проблему для простых одностраничных документов или шаблонов с жестко заданной структурой. Однако он имеет существенный недостаток: если документ станет многостраничным, на последующих страницах замена в нижнем колонтитуле не произойдет, так как там начнет отображаться основной колонтитул с индексом 1.
Чтобы сделать наш алгоритм устойчивым к любым изменениям структуры шаблона (наличию нескольких разделов, разделению на четные/нечетные страницы или особым колонтитулам первой страницы), мы должны применить циклический обход всех возможных областей колонтитулов. Проанализируем следующий программный код:
// Перебираем все разделы документа
Для Каждого Раздел Из Документ.Sections Цикл
// Обходим все типы верхних колонтитулов (их всегда ровно 3 в каждом разделе)
Для Каждого Колонтитул Из Раздел.Headers Цикл
Колонтитул.Range.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
КонецЦикла;
// Обходим все типы нижних колонтитулов
Для Каждого Колонтитул Из Раздел.Footers Цикл
Колонтитул.Range.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
КонецЦикла;
КонецЦикла;
Данный подход гарантирует, что маркер автоподстановки будет найден и заменен абсолютно во всех колонтитулах, независимо от того, на какой странице они выводятся и какие настройки разметки применил пользователь при создании шаблона.
Помимо колонтитулов и основного текста, параметры автозаполнения в шаблонах Word могут находиться в сносках, комментариях или графических надписях (объектах TextBox/Текстовое поле). Чтобы не писать отдельные циклы для каждого типа объектов, мы можем использовать встроенный механизм объектной модели MS Word — коллекцию областей документа StoryRanges.
Каждая область (основной текст, колонтитулы, сноски) представляет собой отдельный элемент в этой коллекции. Давайте рассмотрим профессиональный метод поиска и замены, который гарантированно обработает абсолютно все скрытые уголки нашего файла:
// Перебираем все базовые области документа
Для Каждого Область Из Документ.StoryRanges Цикл
// Выполняем замену в текущей области
Область.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
// Обрабатываем связанные области (например, цепочки текстовых блоков)
ТекущаяОбласть = Область.NextStoryRange;
Пока ТекущаяОбласть <> Неопределено Цикл
ТекущаяОбласть.Find.Execute("{" + ЭлементДанных.Ключ + "}", Ложь, Истина, Ложь, Ложь, , Истина, , Ложь, ЭлементДанных.Значение, 2);
ТекущаяОбласть = ТекущаяОбласть.NextStoryRange;
КонецЦикла;
КонецЦикла;
Этот алгоритм является промышленным стандартом при работе с COM-объектом Word в 1С. Он избавляет от необходимости заботиться о структуре документа и обеспечивает 100% покрытие заменяемых параметров.
Хотя технология COM (использование Новый COMОбъект("Word.Application")) решает задачу, она обладает критическими недостатками в современных реалиях:
Если вы разрабатываете современную конфигурацию, настоятельно рекомендуем обратить внимание на возможности **БСП (Библиотеки стандартных подсистем)**, а именно на модуль УправлениеПечатью. Этот механизм позволяет работать с файлами .docx напрямую как с ZIP-архивами. 1С на уровне сервера распаковывает документ, с помощью быстрых алгоритмов работы с XML модифицирует файлы разметки (включая файлы колонтитулов header1.xml, footer1.xml) и собирает архив обратно. Это работает в десятки раз быстрее, не требует установленного MS Word и полностью совместимо с серверами на Linux.