Печать таблицы в MS Word через БСП: как автоматически переносить строки и повторить шапку без COM-соединения

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

При переходе от классического использования COM-объекта Word.Application на сервере к современным серверным механизмам Библиотеки стандартных подсистем (БСП) разработчики часто сталкиваются с проблемой верстки документов. БСП формирует файлы в формате .docx напрямую через сборку XML-структур внутри ZIP-архива. Это происходит на стороне сервера, зачастую под управлением операционной системы Linux, где физически нет установленного MS Word.

В связи с этим возникает серьезная методическая сложность: в отличие от стандартного табличного документа 1С, для которого предусмотрен метод ПроверитьВывод(), у документов MS Word на сервере нет встроенного инструмента для автоматического расчета высоты страниц. В формате OpenXML понятия «физической страницы» не существует — разбиение текста на листы, перенос строк и расчет высоты ячеек происходят динамически на стороне клиента в тот момент, когда пользователь открывает файл в MS Word, LibreOffice или другом офисном пакете. Разметка зависит от локального принтера, версии офиса, шрифтов операционной системы и масштабирования.

Попытки вручную рассчитать высоту XML-тегов или найти идентификаторы с размерами ячеек не принесут стабильного результата. В этой статье мы подробно рассмотрим, как решить проблему «убегающих» таблиц и пустых пространств на листах с помощью правильной настройки шаблонов MS Word и программных методов 1С.

Способ 1. Использование встроенных свойств разметки таблиц в шаблоне Word (Рекомендуемый)

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

Рассмотрим по шагам три ключевые настройки в шаблоне документа:

  1. Автоматический повтор шапки таблицы на каждой новой странице.

    Если таблица переносится на следующий лист, дублировать шапку вручную через код 1С не нужно. Откроем наш макет в MS Word, выделим строку с шапкой таблицы, нажмем правую кнопку мыши и перейдем в «Свойства таблицы». На вкладке «Строка» установим флаг «Повторять как заголовок на каждой странице» (в структуре OpenXML за это отвечает тег w:tblHeader). Теперь при открытии готового файла MS Word сам определит границы страниц и автоматически добавит шапку вверху каждого листа.

  2. Запрет разрыва строк таблицы (свойство cantSplit).

    Часто бывает, что строка таблицы содержит много текста и разрывается посередине: верхняя часть строки остается на одной странице, а нижняя уходит на другую. Чтобы строка переносилась целиком, выделим всю таблицу в макете, зайдем в «Свойства таблицы» -> вкладка «Строка» -> снимем флаг «Разрешить перенос строк на следующую страницу» (в XML запишется тег w:cantSplit). Если строка не помещается на листе, Word автоматически перенесет ее целиком на новый лист.

  3. Связывание заголовков с таблицей (свойство keepNext).

    Чтобы заголовок таблицы или вводный абзац не оставался «сиротой» внизу страницы, когда сама таблица начинается на следующем листе, применим свойство абзаца. Выделим заголовок перед таблицей, зайдем в свойства абзаца на вкладку «Положение на странице» и установим флаг «Не отрывать от следующего» (в XML добавится тег w:keepNext).

  4. Начало раздела с новой страницы (свойство pageBreakBefore).

    Если после таблицы у нас идет область «Подвал» или «Приложение», которая обязательно должна начинаться с нового листа, не нужно вставлять пустые абзацы или разрывы страниц в коде. В шаблоне Word для первого абзаца этой области включим свойство «Начать с новой страницы» (в XML запишется тег w:pageBreakBefore). При выводе этой области БСП перенесет свойство абзаца, и Word сам сделает разрыв страницы в нужном месте.

Способ 2. Эмпирический расчет строк и использование вспомогательной области разрыва в БСП

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

Для реализации этого метода разберем алгоритм настройки макета и написания кода:

  1. В макете Word создадим отдельную область и назовем ее, например, РазрывСтраницы. Внутри этой области вставим один символ разрыва страницы (в меню Word: Вставка -> Разрыв страницы) и один пустой абзац, так как полностью пустые области БСП может проигнорировать при сборке XML.
  2. В коде 1С перед выводом строк таблицы инициализируем счетчик выведенных строк.
  3. Опытным путем на тестовых данных определим среднее количество строк, которое гарантированно помещается на одном листе (например, 20-25 строк при стандартных шрифтах).
  4. При заполнении табличной части в цикле будем увеличивать счетчик строк. При достижении лимита программно выведем область разрыва страницы, после чего сбросим счетчик в ноль.

Проанализируем пример кода, реализующий данный подход:


// Получаем макет и инициализируем печатную форму методами БСП
МакетДанных = УправлениеПечатью.ПолучитьМакет("ОбщийМакет.НашМакетWord");
ПечатнаяФорма = УправлениеПечатью.ИнициализироватьПечатнуюФормуMSWord(МакетДанных);

ОбластьСтрока = УправлениеПечатью.ОбластьМакета(МакетДанных, "СтрокаТаблицы");
ОбластьРазрыв = УправлениеПечатью.ОбластьМакета(МакетДанных, "РазрывСтраницы");

МаксимумСтрокНаЛисте = 22;
ТекущийСчетчикСтрок = 0;

Для Каждого СтрокаТовара Из СсылкаНаДокумент.Товары Цикл
    
    ДанныеЗаполнения = Новый Структура;
    ДанныеЗаполнения.Вставить("НомерСтроки", СтрокаТовара.НомерСтроки);
    ДанныеЗаполнения.Вставить("Номенклатура", СтрокаТовара.Номенклатура.Наименование);
    ДанныеЗаполнения.Вставить("Количество", СтрокаТовара.Количество);
    ДанныеЗаполнения.Вставить("Цена", СтрокаТовара.Цена);
    ДанныеЗаполнения.Вставить("Сумма", СтрокаТовара.Сумма);
    
    // Присоединяем заполненную строку к печатной форме
    УправлениеПечатью.ПрисоединитьОбласть(ПечатнаяФорма, ОбластьСтрока, ДанныеЗаполнения);
    ТекущийСчетчикСтрок = ТекущийСчетчикСтрок + 1;
    
    // Если лимит строк исчерпан, вставляем разрыв страницы
    Если ТекущийСчетчикСтрок >= МаксимумСтрокНаЛисте Тогда
        УправлениеПечатью.ПрисоединитьОбласть(ПечатнаяФорма, ОбластьРазрыв);
        ТекущийСчетчикСтрок = 0; // Сбрасываем счетчик для следующей страницы
    КонецЕсли;
    
КонецЦикла;

Важное примечание: Этот метод не учитывает, если в одной из строк таблицы текст перенесется на 3-4 строки вниз внутри ячейки. Он эффективен только при фиксированной высоте строк таблицы.

Способ 3. Формирование через Табличный документ с последующей конвертацией в DOCX

Если перед нами стоит задача добиться абсолютной точности вывода «пиксель в пиксель», с расчетом промежуточных итогов по страницам и контролем переноса строк, то лучшим решением будет использование стандартного объекта платформы ТабличныйДокумент с последующим его экспортом в формат .docx.

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

Разберем по шагам реализацию этого метода в коде:


// Создаем стандартный табличный документ 1С
ТабДок = Новый ТабличныйДокумент;
ТабДок.АвтоМасштаб = Истина;
ТабДок.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;

// Получаем области из макета табличного документа
МакетMXL = ПолучитьМакет("МакетПечатиMXL");
ОбластьШапкаТаблицы = МакетMXL.ПолучитьОбласть("ШапкаТаблицы");
ОбластьСтрокаТаблицы = МакетMXL.ПолучитьОбласть("СтрокаТаблицы");

// Выводим шапку таблицы первый раз
ТабДок.Вывести(ОбластьШапкаТаблицы);

Для Каждого СтрокаДанных Из СсылкаНаДокумент.Товары Цикл
    
    // Заполняем параметры области строки
    ЗаполнитьЗначенияСвойств(ОбластьСтрокаТаблицы.Параметры, СтрокаДанных);
    
    // Проверяем, поместится ли строка на текущей странице вместе с шапкой таблицы
    МассивВыводимыхОбластей = Новый Массив;
    МассивВыводимыхОбластей.Добавить(ОбластьСтрокаТаблицы);
    
    Если Не ТабДок.ПроверитьВывод(МассивВыводимыхОбластей) Тогда
        // Если не помещается, принудительно переносим страницу
        ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
        // Повторно выводим шапку на новой странице
        ТабДок.Вывести(ОбластьШапкаТаблицы);
    КонецЕсли;
    
    // Выводим строку в табличный документ
    ТабДок.Вывести(ОбластьСтрокаТаблицы);
    
КонецЦикла;

// Сохраняем готовый документ в формате Word (.docx) без участия MS Word на сервере
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("docx");
ТабДок.Записать(ИмяВременногоФайла, ТипФайлаТабличногоДокумента.Docx);

При использовании этого метода платформа 1С самостоятельно преобразует всю сложную верстку табличного документа, включая горизонтальные разделители страниц, в правильные теги разметки OpenXML. На выходе мы получаем корректный файл .docx, который откроется в любом текстовом редакторе в строгом соответствии с нашими правилами разбиения на страницы.

← На главную