Как заполнить параметры и динамическую таблицу в документе Word формата docx без COM-объекта в 1С

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

При формировании печатных форм и отчетов в формате Microsoft Word на стороне сервера часто возникает ограничение: офисный пакет не установлен на серверной операционной системе (особенно под управлением Linux), либо использование COM-соединения через COMОбъект("Word.Application") работает нестабильно, блокирует интерфейс и расходует много ресурсов. Документ формата docx представляет собой обычный zip-архив, содержащий XML-файлы разметки OpenXML. Разберем по шагам, как программно распаковать такой документ, заполнить обычные реквизиты, динамически сформировать строки таблицы товаров и корректно собрать файл обратно средствами платформы 1С:Предприятие.

Особенности внутреннего формата docx и типичные сложности

Основное содержимое документа Word находится внутри архива в файле word/document.xml. При прямой работе с данным файлом как со строкой разработчики часто сталкиваются с неочевидными проблемами OpenXML, которые приводят к ошибкам при открытии файла:

  1. Дробление текста на мелкие фрагменты (runs): при вводе текста в шаблоне Word разбивает строки на отдельные узлы <w:r> и <w:t>. Если вы ввели метку {Товар}, в XML она может превратиться в цепочку из трех узлов: <w:t>{</w:t>, <w:t>Товар</w:t> и <w:t>}</w:t>. Простой вызов метода СтрЗаменить() в таком случае не найдет метку. Для предотвращения дробления рекомендуется вставлять метки в шаблон единым блоком из буфера обмена и отключать проверку правописания для меток.
  2. Обязательное экранирование символов: любые строковые данные из информационной базы (наименования, артикулы, комментарии), содержащие символы &, <, >, ", ', нарушают валидность XML. Перед подстановкой значений их необходимо обязательно экранировать (например, заменять & на &amp;).
  3. Строгая структура таблиц: каждая строка таблицы оформляется тегом <w:tr>, ячейка — тегом <w:tc>. Внутри ячейки обязательно должен присутствовать параграф <w:p>. Если ячейка пустая, параграф внутри нее все равно должен быть объявлен, иначе Word сочтет файл поврежденным.

Способ 1. Использование встроенных механизмов БСП

Проанализируем конфигурации на базе Библиотеки стандартных подсистем (включая 1С:КА, 1С:ERP, 1С:УТ, 1С:ДО). В подсистеме печати БСП уже реализован готовый механизм формирования документов docx без COM-объекта:

  1. В макете документа настраиваются специальные области для шапки, повторяющихся строк и подвала (с использованием текстовых меток или элементов управления содержимым Content Controls).
  2. Программный интерфейс общих модулей печати БСП считывает макет, автоматически дублирует строку-образец нужное количество раз, заполняет параметры переданной структуры данных и формирует готовый файл.

Этот способ является наиболее предпочтительным и безопасным, так как штатные механизмы БСП берут на себя контроль валидности структуры XML и работу с архивом.

Способ 2. Вставка сформированного XML-блока таблицы целиком

Если требуется нестандартная верстка, колонки разной ширины, объединенные строки итогов или перенос логики в собственную конфигурацию, эффективным решением является замена единого параметра в шаблоне на готовый XML-код всей таблицы. Разберем этот алгоритм подробно.

Чтобы понять точную XML-структуру сложной таблицы, создадим в Microsoft Word пустой документ, добавим требуемую таблицу с нужным оформлением (ширина столбцов, шрифты, границы, объединение ячеек в подвале) и сохраним файл. Затем разархивируем docx и изучим файл word/document.xml:

  1. В свойствах ячейки <w:tcPr> объединение колонок по горизонтали задается тегом <w:gridSpan w:val="N"/>, где N — количество объединенных столбцов.
  2. Объединение по вертикали выполняется тегом <w:vMerge w:val="restart"/> в первой ячейке и <w:vMerge/> в последующих строках той же колонки.
  3. Служебные атрибуты идентификаторов ревизий (например, w:rsidR, w:rsidTr) можно безопасно исключить из генерации, чтобы не перегружать XML.

Рассмотрим пример кода 1С, который распаковывает архив, генерирует XML-таблицу товаров и выполняет замену маркера:


// 1. Распаковка архива docx во временный каталог
КаталогВременныхФайловШаблона = ПолучитьИмяВременногоФайла();
СоздатьКаталог(КаталогВременныхФайловШаблона);

ЧтениеАрхива = Новый ЧтениеZipФайла(ПутьКШаблонуDocx);
ЧтениеАрхива.ИзвлечьВсе(КаталогВременныхФайловШаблона, РежимВосстановленияПутейФайловZIP.Восстанавливать);
ЧтениеАрхива.Закрыть();

ПутьКDocumentXML = КаталогВременныхФайловШаблона + "/word/document.xml";

// 2. Чтение исходного XML документа
ТекстовыйФайл = Новый ТекстовыйДокумент;
ТекстовыйФайл.Прочитать(ПутьКDocumentXML, КодировкаТекста.UTF8);
ТекстДокумента = ТекстовыйФайл.ПолучитьТекст();

// 3. Формирование XML-кода таблицы товаров
СтрТаблица = "";
СтрТаблица = СтрТаблица + "<w:tbl><w:tblPr><w:tblW w:w=""0"" w:type=""auto""/></w:tblPr>";

// Шапка таблицы
СтрТаблица = СтрТаблица + "<w:tr>"
    + "<w:tc><w:p><w:r><w:rPr><w:b/></w:rPr><w:t>№</w:t></w:r></w:p></w:tc>"
    + "<w:tc><w:p><w:r><w:rPr><w:b/></w:rPr><w:t>Товар</w:t></w:r></w:p></w:tc>"
    + "<w:tc><w:p><w:r><w:rPr><w:b/></w:rPr><w:t>Количество</w:t></w:r></w:p></w:tc>"
    + "<w:tc><w:p><w:r><w:rPr><w:b/></w:rPr><w:t>Сумма</w:t></w:r></w:p></w:tc>"
    + "</w:tr>";

// Строки с товарами
ИтогоСумма = 0;
Для Каждого Стр Из ТаблицаТоваров Цикл
    ИтогоСумма = ИтогоСумма + Стр.Сумма;
    
    // Экранируем спецсимволы в наименовании
    БезопасноеНаименование = СтрЗаменить(Стр.Номенклатура, "&", "&amp;");
    БезопасноеНаименование = СтрЗаменить(БезопасноеНаименование, "<", "&lt;");
    БезопасноеНаименование = СтрЗаменить(БезопасноеНаименование, ">", "&gt;");
    
    СтрТаблица = СтрТаблица + "<w:tr>"
        + "<w:tc><w:p><w:r><w:t>" + Стр.НомерСтроки + "</w:t></w:r></w:p></w:tc>"
        + "<w:tc><w:p><w:r><w:t>" + БезопасноеНаименование + "</w:t></w:r></w:p></w:tc>"
        + "<w:tc><w:p><w:r><w:t>" + Формат(Стр.Количество, "ЧДЦ=2") + "</w:t></w:r></w:p></w:tc>"
        + "<w:tc><w:p><w:r><w:t>" + Формат(Стр.Сумма, "ЧДЦ=2") + "</w:t></w:r></w:p></w:tc>"
        + "</w:tr>";
КонецЦикла;

// Итоговая строка с объединением трех колонок
СтрТаблица = СтрТаблица + "<w:tr>"
    + "<w:tc><w:tcPr><w:gridSpan w:val=""3""/></w:tcPr><w:p><w:r><w:rPr><w:b/></w:rPr><w:t>ИТОГО:</w:t></w:r></w:p></w:tc>"
    + "<w:tc><w:p><w:r><w:rPr><w:b/></w:rPr><w:t>" + Формат(ИтогоСумма, "ЧДЦ=2") + "</w:t></w:r></w:p></w:tc>"
    + "</w:tr>";

СтрТаблица = СтрТаблица + "</w:tbl>";

// 4. Подстановка таблицы вместо метки в документе
// Метка в шаблоне должна быть обернута отдельным параграфом, который замещается таблицей
ТекстДокумента = СтрЗаменить(ТекстДокумента, "<w:p><w:r><w:t>[ТаблицаТоваров]</w:t></w:r></w:p>", СтрТаблица);

// Заполнение обычных реквизитов документа
ТекстДокумента = СтрЗаменить(ТекстДокумента, "[НомерДокумента]", НомерДокумента);
ТекстДокумента = СтрЗаменить(ТекстДокумента, "[ДатаДокумента]", Формат(ДатаДокумента, "ДЛФ=DD"));

// 5. Запись обновленного XML
ТекстовыйФайл.УстановитьТекст(ТекстДокумента);
ТекстовыйФайл.Записать(ПутьКDocumentXML, КодировкаТекста.UTF8);

// 6. Сборка архива docx обратно
ЗаписьАрхива = Новый ЗаписьZipФайла(ПутьКФинальномуDocx);
ЗаписьАрхива.Добавить(КаталогВременныхФайловШаблона + "/*", РежимСохраненияПутейZIP.СохранятьОтносительныеПути, РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно);
ЗаписьАрхива.Записать();

// Удаление временных файлов
УдалитьФайлы(КаталогВременныхФайловШаблона);

Способ 3. Модификация строк через объектную модель DOM

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

  1. В шаблоне создается готовая таблица, в которой присутствует заголовок и одна строка-образец с метками в ячейках (например, [Номер], [Номенклатура], [Количество]).
  2. Файл word/document.xml загружается через ЧтениеXML и объект ПостроительDOM.
  3. По структуре узлов выполняется поиск узла <w:tr>, содержащего наши метки.
  4. Для каждой строки таблицы значений узел-образец клонируется вызовом Клонировать(Истина).
  5. В склонированном узле изменяются значения дочерних текстовых элементов, после чего новая строка добавляется в родительский узел таблицы методом ВставитьПеред().
  6. Исходная строка-шаблон удаляется из дерева методом УдалитьВложенный().
  7. Итоговый документ сохраняется через ЗаписьDOM и ЗаписьXML.

Работа через DOM исключает синтаксические ошибки закрытия тегов и повреждение разметки, однако для документов объемом в сотни страниц требует повышенного объема оперативной памяти.

Альтернативные кроссплатформенные варианты

Если задача не требует жесткого соответствия специфическим возможностям верстки Microsoft Word, рассмотрим другие стандартные способы выгрузки данных:

  1. Стандартный табличный документ 1С: макет создается в конфигураторе или режиме предприятия, заполняется через штатные области и параметры, а затем выгружается методом ТабличныйДокумент.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.DOCX) или ТипФайлаТабличногоДокумента.PDF. Метод работает полностью нативно, без COM и офисных пакетов на серверах Windows и Linux.
  2. Генерация HTML: формируется простой HTML-файл со стандартными тегами <table>, <tr>, <td> и сохраняется с расширением .doc. Microsoft Word открывает такой документ прозрачно для пользователя.
← На главную