При формировании печатных форм в 1С часто возникает задача сформировать объемный документ с жестким постраничным разбиением: вывести блок строк, проверить помещаемость на листе, при необходимости зафиксировать промежуточные итоги (тоталы) и перенести следующий блок на новую страницу. Разработчики привыкли решать эту задачу связкой методов ПроверитьВывод() и ВывестиГоризонтальныйРазделительСтраниц(). Однако при попытке сохранить полученный ТабличныйДокумент в формат Microsoft Word (DOCX) все заданные разрывы страниц пропадают, а весь текст объединяется в сплошную простыню. Разберем по шагам, почему это происходит и какими способами можно добиться корректного разделения листов в Word.
Для понимания корня проблемы проанализируем механизм конвертации табличного документа в форматы сторонних офисных пакетов:
ТабДок.ВывестиГоризонтальныйРазделительСтраниц() и свойство области КонецСтраницы разработаны исключительно для встроенного механизма рендеринга 1С — вывода на печать, просмотра внутри платформы и сохранения в форматы MXL или PDF.ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.DOCX) платформа преобразует координатную сетку ячеек 1С в одну сплошную таблицу Word (тег <w:tbl> в формате OpenXML).<w:br w:type="page"/>) непосредственно между строками одной и той же таблицы невозможно без полного физического рассечения таблицы на две отдельные. Поэтому внутренний конвертер 1С полностью игнорирует добавленные горизонтальные разделители.Рассмотрим рабочие альтернативы, позволяющие получить корректный документ Word с четкой разбивкой по страницам.
Если для бизнес-логики принципиально иметь документ в формате Word и выводить промежуточные итоги, самым гибким подходом является создание документа напрямую через Automation-сервер. Для этого создадим COM-объект и настроим правила переноса строк таблицы.
Посмотрим на пример базовой инициализации документа из 1С:
Word1C = Новый COMОбъект("Word.Application");
Word1C.Visible = Ложь;
НовыйДокумент = Word1C.Documents.Add();
В модели Word существует несколько ключевых параметров, позволяющих управлять разрывами строк и абзацев:
AllowBreakAcrossPages = Ложь. В этом случае вся строка целиком автоматически перенесется на следующий лист, если не помещается внизу страницы.
Ячейка = ТаблицаWord.Cell(НомерСтроки, 1);
Ячейка.Range.ParagraphFormat.PageBreakBefore = Истина;
<w:pageBreakBefore/>, оставляя таблицу целостной, но отправляя нужную строку на новую страницу.KeepWithNext = Истина.В объектной модели Word нет прямого аналога функции ТабДок.ПроверитьВывод(), так как компоновка страниц в текстовом процессоре рассчитывается «на лету» в зависимости от драйвера печати и параметров шрифта. Однако мы можем динамически считывать номер текущей страницы через свойство Range.Information.
Константа wdActiveEndPageNumber имеет числовое значение 3. Разберем алгоритм отслеживания перехода на новую страницу:
ТекущаяСтраница = 1;
Для Каждого СтрДанных Из ТаблицаДанных Цикл
// Добавляем строку в таблицу Word
НоваяСтрока = ТаблицаWord.Rows.Add();
НоваяСтрока.AllowBreakAcrossPages = Ложь;
НоваяСтрока.Cells(1).Range.Text = СтрДанных.ТекстПараграфа;
// Проверяем, на какой физической странице оказалась ячейка
ФактическаяСтраница = НоваяСтрока.Cells(1).Range.Information(3);
Если ФактическаяСтраница > ТекущаяСтраница Тогда
// Произошел переход на новый лист
// Здесь можно отменить последнюю строку, вывести промежуточный итог
// и перенести вывод данных дальше
ТекущаяСтраница = ФактическаяСтраница;
КонецЕсли;
КонецЦикла;
Такой подход позволяет динамически подсчитывать итоги по каждой странице документа Word непосредственно в процессе генерации.
Если на сервере 1С не установлен Microsoft Office или генерация через COM работает слишком медленно, можно воспользоваться структурой файла .docx, который по своей сути представляет собой обычный ZIP-архив.
Рассмотрим по шагам этот вариант:
ВывестиГоризонтальныйРазделительСтраниц() выводим в отдельную строку текстовый маркер-метку, например, ###PAGE_BREAK###.ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.DOCX).ЧтениеZipФайла распаковываем полученный файл во временный каталог.word/document.xml через ЧтениеТекста или ЧтениеXML.<w:p><w:r><w:br w:type="page"/></w:r></w:p> и открытие новой таблицы, либо добавляем тег <w:pageBreakBefore/> в свойства абзаца ячейки (<w:pPr>).ЗаписьZipФайла с расширением .docx.Этот метод работает автономно на стороне сервера Linux или Windows без использования COM-объектов.
Проанализируем еще одну простую и надежную альтернативу. Если основная цель задачи — гарантировать неизменность макета и точное соответствие печатной форме 1С (где ПроверитьВывод() и разделители отработали штатно), целесообразно выгружать результат в PDF:
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.PDF);
Формат PDF гарантирует сохранение всех колонтитулов, разрывов страниц и промежуточных итогов в точности так, как их рассчитала платформа 1С. При необходимости редактирования современные версии офисных пакетов позволяют открывать PDF-документы и автоматически преобразовывать их в формат Word с сохранением разбиения на страницы.