При разработке печатных форм в системе «1С:Предприятие 8» программисты часто сталкиваются с ситуацией, когда один отчет или пакет документов должен состоять из нескольких разделов с разной ориентацией страниц. Например, первый раздел (титульный лист и общие сведения) должен быть портретным, а второй раздел (большая спецификация или аналитическая таблица) — ландшафтным.
Поскольку параметры страницы (ориентация, поля, масштаб) в 1С задаются для всего объекта ТабличныйДокумент целиком, объединить такие разделы в один документ невозможно. Нам приходится формировать два отдельных табличных документа. Но как в таком случае обеспечить красивую сквозную нумерацию страниц в формате «Страница X из Y» во всех документах пакета? В этой статье мы подробно разберем, как решить эту задачу стандартными средствами платформы 1С.
Если мы используем стандартный механизм колонтитулов табличного документа, то для вывода номеров страниц обычно применяются специальные шаблоны-макросы:
[&НомерСтраницы] — динамически выводит номер текущей страницы документа.[&СтраницВсего] — динамически выводит общее количество страниц в документе.Когда документов несколько, эти макросы работают изолированно внутри каждого из них. Первый документ начнется с первой страницы и покажет общее число страниц только для своей части. Второй документ также начнется с первой страницы и покажет общее количество страниц только для себя. В результате пользователь получит разрозненные документы с несвязанной нумерацией.
Для решения этой проблемы нам необходимо решить две задачи:
[&СтраницВсего].Выясним, как это реализовать по шагам.
Начиная с версии платформы 1С:Предприятие 8.3.12, у объекта ТабличныйДокумент появилось замечательное свойство — НомерПервойСтраницы. Оно позволяет явно указать начальное число для отсчета страниц при выводе колонтитулов.
По умолчанию это свойство равно Неопределено (или 0), что означает стандартный отсчет с 1. Если же мы программно присвоим этому свойству значение, например, 3, то первая страница этого табличного документа получит в колонтитуле номер 3, вторая — 4 и так далее.
Проанализируем пример кода:
// Задаем начальный номер страницы для второго документа
ТабДок2.НомерПервойСтраницы = 3;
Это свойство отлично работает не только при прямой печати из 1С, но и при экспорте табличного документа в сторонние форматы, такие как XLSX (Open XML) или ODS (OpenDocument). Приложение, открывающее файл (например, Microsoft Excel), считает эту настройку и продолжит нумерацию корректно.
Макрос [&СтраницВсего] не умеет самостоятельно суммировать страницы из разных объектов ТабличныйДокумент. Поэтому нам придется отказаться от его автоматического использования. Вместо этого мы будем рассчитывать общее число страниц программно и подставлять его в текст колонтитула как обычную строку.
Для этого мы воспользуемся методом КоличествоСтраниц() объекта ТабличныйДокумент. Этот метод принудительно рассчитывает, сколько физических листов бумаги займет документ при печати с текущими настройками принтера, масштаба и полей.
Рассмотрим детальный алгоритм и код реализации этой логики:
// 1. Полностью формируем и заполняем оба табличных документа
// (код заполнения документов опущен для краткости)
// 2. Получаем количество страниц в каждом документе
СтраницВПервом = ТабДок1.КоличествоСтраниц();
СтраницВоВтором = ТабДок2.КоличествоСтраниц();
// Вычисляем общую сумму страниц для сквозного колонтитула
ВсегоСтраниц = СтраницВПервом + СтраницВоВтором;
// 3. Настраиваем колонтитулы для первого документа
ТабДок1.НижнийКолонтитул.Выводить = Истина;
// Используем динамический номер страницы и статическое общее количество
ТабДок1.НижнийКолонтитул.ТекстВЦентре = "Страница [&НомерСтраницы] из " + Строка(ВсегоСтраниц);
// 4. Настраиваем смещение и колонтитулы для второго документа
ТабДок2.НомерПервойСтраницы = СтраницВПервом + 1;
ТабДок2.НижнийКолонтитул.Выводить = Истина;
ТабДок2.НижнийКолонтитул.ТекстВЦентре = "Страница [&НомерСтраницы] из " + Строка(ВсегоСтраниц);
В результате выполнения этого кода мы получим идеальную сквозную нумерацию. Если первый документ занимает 2 страницы, а второй — 4 страницы, то колонтитулы распределятся следующим образом:
Разберем один важный нюанс, с которым часто сталкиваются разработчики при работе на серверной стороне (в фоновых заданиях или серверных модулях). Метод КоличествоСтраниц() выполняет расчет на основе параметров принтера по умолчанию, установленного в операционной системе.
Если код выполняется на сервере 1С:Предприятия, под учетной записью службы сервера должен быть установлен хотя бы один принтер (пусть даже виртуальный, например, Microsoft XPS Document Writer). Если принтер по умолчанию на сервере отсутствует или недоступен, метод КоличествоСтраниц() может вернуть 0 или вызвать ошибку выполнения. Всегда учитывайте этот момент при проектировании серверной печати.
Если мы просто по очереди отправим два документа на принтер методами ТабДок1.Напечатать() и ТабДок2.Напечатать(), система создаст два независимых задания печати. Это неудобно по нескольким причинам:
Чтобы избежать этих проблем, мы воспользуемся специальным объектом платформы — ПакетОтображаемыхДокументов. Этот объект позволяет объединить несколько табличных документов с сохранением их индивидуальных параметров страницы (включая разную ориентацию листов) в один пакет для печати или выгрузки.
Посмотрим на пример кода, который объединяет наши документы в пакет и сохраняет его в один PDF-файл с сохранением сквозной нумерации и ориентации страниц:
// Создаем объект пакета документов
Пакет = Новый ПакетОтображаемыхДокументов;
// Помещаем первый табличный документ во временное хранилище и добавляем в пакет
АдресВХ1 = ПоместитьВоВременноеХранилище(ТабДок1);
ЭлементПакета1 = Пакет.Состав.Добавить(АдресВХ1);
ЭлементПакета1.Имя = "Часть_1_Портретная";
// Помещаем второй табличный документ во временное хранилище и добавляем в пакет
АдресВХ2 = ПоместитьВоВременноеХранилище(ТабДок2);
ЭлементПакета2 = Пакет.Состав.Добавить(АдресВХ2);
ЭлементПакета2.Имя = "Часть_2_Ландшафтная";
// Вариант 1. Отправляем пакет на печать одним заданием
Пакет.Напечатать(Истина); // Параметр Истина показывает диалог выбора принтера один раз для всего пакета
// Вариант 2. Сохраняем весь пакет в один многостраничный PDF-файл
ИмяФайлаPDF = ПолучитьИмяВременногоФайла("pdf");
Пакет.Записать(ИмяФайлаPDF, ТипФайлаПакетаОтображаемыхДокументов.PDF);
Использование ПакетОтображаемыхДокументов гарантирует, что при печати на физическом принтере или при сохранении в PDF-файл ландшафтные страницы останутся ландшафтными, портретные — портретными, а сквозная нумерация колонтитулов не собьется.
Мы успешно решили задачу сквозной нумерации колонтитулов для документов с разной структурой и ориентацией страниц. Подведем итоги нашего разбора:
НомерПервойСтраницы для смещения стартового номера во втором и последующих документах.[&СтраницВсего] в пользу программного сложения результатов метода КоличествоСтраниц() всех выводимых документов.ПакетОтображаемыхДокументов.