Как реализовать печать нескольких макетов в один файл Word из 1С?

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

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

Анализ исходной проблемы

Рассмотрим ситуацию: у нас есть цикл, в котором для каждой строки документа определяется подходящий макет (например, один для изменения должности, другой для изменения оклада). В исходном коде создание объекта Word.Application и метода Documents.Add происходит внутри цикла. Это крайне неэффективно, так как каждый раз запускается новый процесс Word, что потребляет значительные ресурсы системы и не позволяет получить единый печатный лист.

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

Шаг 1: Оптимизация работы с COM-объектом

Первым делом вынесем создание приложения Word и основного документа за пределы цикла. Это позволит нам работать в контексте одного процесса. Проанализируем, как должна измениться структура процедуры:


&НаКлиенте
Процедура ОбработкаКоманды(ПараметрКоманды, ПараметрыВыполненияКоманды)
    СтруктураДокумента = ПолучитьСтруктуруДокументаНаСервере(ПараметрКоманды);
    
    // Создаем приложение Word ОДИН РАЗ перед циклом
    Попытка
        Word = Новый COMОбъект("Word.Application");
        Word.Visible = Ложь; // Скрываем работу для ускорения процесса
    Исключение
        Сообщить("Ошибка при запуске Word: " + ОписаниеОшибки());
        Возврат;
    КонецПопытки;

    // Создаем основной результирующий документ
    РезультатДокумент = Word.Documents.Add();

    // Цикл по данным
    Для ц = 0 По СтруктураДокумента.Количество() - 1 Цикл
        // Здесь будет логика добавления данных из макетов
    КонецЦикла;

    Word.Visible = Истина; // Показываем готовый результат пользователю
КонецПроцедуры

Шаг 2: Использование метода InsertFile для объединения макетов

Одним из самых надежных способов объединения разных документов Word является метод InsertFile. Рассмотрим подробнее этот подход. Вместо того чтобы пытаться копировать текст через буфер обмена (который может быть занят другими программами), мы будем подготавливать временный файл для каждой строки и «вклеивать» его в конец основного документа.

Разберем по шагам алгоритм внутри цикла:

  1. Получаем путь к временному файлу макета (как это и было в исходном коде).
  2. Создаем временный документ на основе этого макета.
  3. Производим в нем замену всех параметров (ФИО, Должность, Оклад и т.д.).
  4. Сохраняем временный документ и закрываем его.
  5. В основном документе переходим в самый конец и вызываем InsertFile.
  6. Вставляем разрыв страницы, чтобы следующая запись начиналась с нового листа.

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


// Внутри цикла...
ПараметрыДокумента = СтруктураДокумента.Получить(ц);
ПутьКМакету = МакетПолучить(?(ПараметрыДокумента.ДолжностьНовая <> ПараметрыДокумента.Должность, "Изменение должности", ""));

Если ЗначениеЗаполнено(ПутьКМакету) Тогда
    // Открываем временный документ для заполнения параметров
    ВременныйДок = Word.Documents.Open(ПутьКМакету);
    
    // Выполняем замену параметров (функцию замены рассмотрим ниже)
    ЗаполнитьПараметрыВДокументе(ВременныйДок, ПараметрыДокумента);
    
    // Сохраняем изменения и закрываем временный файл
    ВременныйДок.Save();
    ВременныйДок.Close();
    
    // Переходим в конец основного документа
    Word.Selection.EndKey(6); // 6 = wdStory (переход в конец документа)
    
    // Если это не первая итерация, вставляем разрыв страницы
    Если ц > 0 Тогда
        Word.Selection.InsertBreak(7); // 7 = wdPageBreak
    КонецЕсли;
    
    // Вставляем содержимое временного файла в основной
    Word.Selection.InsertFile(ПутьКМакету);
    
    // Удаляем временный файл
    УдалитьФайлы(ПутьКМакету);
КонецЕсли;

Шаг 3: Проблема длинных строк (более 255 символов)

При использовании стандартного метода Execute объекта Find в Word, вы можете столкнуться с ограничением: строка замены не может превышать 255 символов. Проанализируем ситуацию: если в параметре передается длинный текст (например, должностная инструкция), 1С выдаст ошибку.

Для решения этой проблемы выясним причину: метод Execute работает через параметры команды, которые имеют жесткое ограничение. Вместо этого рекомендуем использовать свойство Range.Text. Выясним, как это реализовать:


Процедура ЗаполнитьПараметрыВДокументе(Документ, СтруктураПараметров)
    Для Каждого Параметр Из СтруктураПараметров Цикл
        Поиск = Документ.Content.Find;
        Поиск.Text = "[" + Параметр.Ключ + "]";
        
        Пока Поиск.Execute() Цикл
            // Используем Range для замены, чтобы обойти лимит 255 символов
            Поиск.Parent.Text = Строка(Параметр.Значение);
            // После замены Range сбрасывается, нужно переинициализировать поиск
            // или использовать более сложные конструкции, если параметров много
        КонецЦикла;
    КонецЦикла;
КонецПроцедуры

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

Шаг 4: Альтернативный метод (Copy-Paste)

Если по каким-то причинам использование временных файлов и метода InsertFile неудобно, можно воспользоваться программным копированием через буфер обмена. Рассмотрим этот вариант подробнее, так как он часто встречается в советах на форумах.

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

  1. Открываем макет.
  2. Заполняем параметры.
  3. Выделяем весь текст макета (Ctrl+A аналог).
  4. Копируем в буфер (Copy).
  5. Закрываем макет.
  6. Вставляем в основной документ (Paste).

Пример кода для этого метода:


// Замена метода InsertFile на Copy/Paste
ВременныйДок = Word.Documents.Open(ПутьКМакету);
// ... заполнение параметров ...
ВременныйДок.Content.Copy(); 
ВременныйДок.Close(0); // Закрыть без сохранения изменений

Word.Selection.EndKey(6);
Если ц > 0 Тогда
    Word.Selection.InsertBreak(7);
КонецЕсли;
Word.Selection.Paste();

Предупреждение: Данный метод менее стабилен при работе на сервере или при одновременной работе пользователя с другими приложениями, так как буфер обмена — это общий ресурс операционной системы. InsertFile является более "чистым" инженерным решением.

Завершение работы и очистка ресурсов

После завершения цикла формирования документа крайне важно корректно завершить работу с COM-объектом. Если в процессе возникла ошибка, Word может остаться висеть в процессах ОС, занимая память. Всегда используйте блоки Попытка...Исключение и не забывайте закрывать приложение в случае критических сбоев, либо делайте его видимым, чтобы пользователь мог закрыть его самостоятельно.

Также помните, что функция ПолучитьИмяВременногоФайла() создает файл в системной папке Temp. Хотя мы вызываем УдалитьФайлы(), хорошей практикой будет сбор всех путей во временный массив и дополнительная очистка в конце процедуры.

Подведем итог: для печати из двух и более макетов в один файл Word из 1С, мы создаем один экземпляр Word.Application, один пустой документ-контейнер и последовательно наполняем его содержимым макетов с помощью метода InsertFile, предварительно заменяя в них нужные нам параметры.

← На главную