В процессе автоматизации документооборота часто возникает задача модификации готовых документов Microsoft Word. Одной из специфических и сложных задач является вставка определенного элемента (например, штампа, надписи или логотипа) на каждую страницу документа. Обычная вставка текста здесь не подходит, так как она может нарушить существующую разметку, а использование OpenXML требует глубокого понимания структуры документа и не всегда доступно при работе с форматом .doc.
В данной статье мы разберем, как реализовать эту задачу с помощью COM-соединения (OLE) между 1С и MS Word, рассмотрим способы навигации по страницам и выясним, как правильно позиционировать элементы, чтобы они не «разваливали» текст.
Для начала взаимодействия нам необходимо создать COM-объект Word.Application. Мы должны предусмотреть открытие документа и подготовить среду для работы. Важным моментом является отключение перерисовки экрана, что значительно ускорит процесс обработки больших файлов.
Рассмотрим пример инициализации:
// Создаем объект приложения Word
Попытка
WordApp = Новый COMОбъект("Word.Application");
Исключение
Сообщить("MS Word не установлен на данном компьютере!");
Возврат;
КонецПопытки;
// Отключаем видимость и обновление экрана для ускорения
WordApp.Visible = Ложь;
WordApp.ScreenUpdating = Ложь;
// Открываем документ
Документ = WordApp.Documents.Open(ПутьКФайлу);
Проблема многих разработчиков заключается в том, что свойство Information(3) (текущая страница) часто возвращает 1, если документ только что открыт и Word еще не успел выполнить фоновую разметку (pagination). Чтобы получить актуальные данные, мы должны принудительно заставить Word пересчитать страницы.
Проанализируем метод ComputeStatistics, который позволяет получить точное количество страниц:
// Принудительно обновляем разметку документа
Документ.Repaginate();
// Получаем количество страниц (константа 2 соответствует wdStatisticPages)
КоличествоСтраниц = Документ.ComputeStatistics(2);
Сообщить("Всего страниц для обработки: " + КоличествоСтраниц);
Важный нюанс: Перед итерацией всегда используйте Repaginate(), особенно если документ содержит сложные таблицы или изображения, которые могут динамически менять высоту контента.
Для того чтобы вставить объект именно на нужную страницу, нам нужно переместить курсор (объект Selection) в начало каждой страницы. Рассмотрим использование метода GoTo, который является наиболее надежным способом перемещения внутри COM-модели Word.
Для вызова GoTo нам понадобятся числовые значения констант VBA:
wdGoToPage = 1wdGoToAbsolute = 1
Для НомерСтраницы = 1 По КоличествоСтраниц Цикл
// Перемещаем курсор на начало текущей страницы
WordApp.Selection.GoTo(1, 1, НомерСтраницы);
// Здесь будет код вставки объекта
// ...
КонецЦикла;
Вместо использования сложных "Строительных блоков" (Building Blocks), которые зависят от версии и языка Office (например, "Simple Text Box" против "Простая надпись"), мы воспользуемся методом прямого создания объекта Shapes.AddTextbox. Это гарантирует работоспособность кода в любой локализации Word.
Разберем параметры вставки: нам нужно задать координаты (отступ слева и сверху), ширину и высоту поля. Чтобы надпись была "прозрачной" для основного текста и находилась поверх него, мы настроим параметры обтекания.
// Настраиваем параметры вставки (в пунктах, 1 см ≈ 28.35 пунктов)
ОтступСлева = 400; // Позиция по горизонтали
ОтступСверху = 700; // Позиция по вертикали
Ширина = 150;
Высота = 50;
// Вставляем текстовое поле
Надпись = Документ.Shapes.AddTextbox(1, ОтступСлева, ОтступСверху, Ширина, Высота);
// Устанавливаем текст и форматирование
Надпись.TextFrame.TextRange.Text = "Штамп: ОДОБРЕНО";
Надпись.TextFrame.TextRange.Font.Size = 10;
Надпись.TextFrame.TextRange.Font.Name = "Arial";
// Настраиваем обтекание (5 - перед текстом, 3 - сквозь текст)
Надпись.WrapFormat.Type = 5;
// Устанавливаем относительное позиционирование, чтобы поле не "уплыло"
Надпись.RelativeHorizontalPosition = 1; // Относительно страницы
Надпись.RelativeVerticalPosition = 1; // Относительно страницы
Если задача состоит в том, чтобы одна и та же надпись отображалась на каждой странице в одном и том же месте, перебор всех страниц в цикле через COM — не самый эффективный путь. Работа через OLE происходит медленно из-за межпроцессного взаимодействия. Проанализируем более быстрый вариант — использование колонтитулов (Header/Footer).
Преимущество: Объект, вставленный в колонтитул с абсолютным позиционированием, визуально может находиться в любой части страницы, но при этом Word будет отрисовывать его автоматически на каждом листе без необходимости цикла.
// Работаем с первым разделом документа
Раздел = Документ.Sections(1);
// Получаем основной колонтитул
Колонтитул = Раздел.Headers(1); // 1 - wdHeaderFooterPrimary
// Вставляем поле прямо в диапазон колонтитула
Надпись = Колонтитул.Shapes.AddTextbox(1, 450, 750, 100, 30);
Надпись.TextFrame.TextRange.Text = "Экземпляр №1";
// Если в документе включены разные колонтитулы для первой страницы
Если Раздел.PageSetup.DifferentFirstPageHeaderFooter Тогда
КолонтитулПервой = Раздел.Headers(2); // wdHeaderFooterFirstPage
// Дублируем вставку для первой страницы
КонецЕсли;
После завершения всех манипуляций крайне важно вернуть настройки приложения в исходное состояние и корректно закрыть соединение, чтобы процесс WINWORD.EXE не остался "висеть" в памяти сервера или клиентского ПК.
WordApp.ScreenUpdating = Истина;Документ.Save(); или Документ.SaveAs(НовыйПуть);WordApp.Quit();Неопределено всем COM-объектам.Выясним основные причины возможных ошибок: если документ защищен от редактирования или открыт другим пользователем в режиме эксклюзивного доступа, COM-вызовы вернут ошибку. Всегда оборачивайте блок работы с Word в конструкцию Попытка ... Исключение.
Используя предложенные методы (ComputeStatistics для расчета страниц и Shapes.AddTextbox для вставки), вы сможете надежно управлять структурой документа Word из 1С, создавая профессиональные печатные формы с необходимыми элементами маркировки.