Как правильно объединить несколько PDF-файлов в один в 1С:Предприятии?

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

При автоматизации документооборота в 1С часто возникает задача объединения нескольких сгенерированных PDF-файлов (например, расходной накладной, счета и сертификатов соответствия) в один многостраничный документ для последующей отправки клиенту по электронной почте. Попытка решить эту задачу «в лоб» с помощью стандартного метода платформы ОбъединитьФайлы() или бинарного сложения через командную строку Windows (команда copy /b) приводит к неожиданному результату: размер итогового файла увеличивается, но при открытии отображается только первая страница или первый документ. В этой статье мы подробно разберем, почему так происходит, и рассмотрим рабочие способы корректной склейки PDF-документов.

Почему простой бинарный способ и метод ОбъединитьФайлы не работают?

Выясним причину, по которой обычное слияние байтов оказывается неэффективным для формата PDF. Дело в том, что PDF (Portable Document Format) — это не просто последовательный поток текстовых или графических данных, а сложный структурированный объектный файл. Его структура включает в себя следующие обязательные элементы:

  1. Заголовок (Header): указывает версию спецификации PDF.
  2. Тело документа (Body): содержит набор объектов (текст, шрифты, векторная графика, изображения).
  3. Таблица перекрестных ссылок (Cross-Reference Table или xref): важнейший элемент, содержащий точные физические смещения (в байтах) каждого объекта от начала файла. Она позволяет программам-просмотрщикам мгновенно переходить к любой странице без последовательного чтения всего документа.
  4. Трейлер (Trailer): указывает программе, где именно начинается таблица xref, а также содержит уникальный идентификатор документа и ссылку на корневой объект.

Программы для чтения PDF (такие как Adobe Acrobat Reader, Foxit Reader или встроенные просмотрщики в веб-браузерах) всегда начинают обработку файла с самого конца. Они ищут маркер конца файла (EOF), считывают трейлер, находят таблицу xref и по ней строят дерево объектов.

Когда мы пытаемся применить метод ОбъединитьФайлы() или команду copy /b file1.pdf + file2.pdf result.pdf, второй файл просто дописывается в самый хвост первого. Программа-ридер открывает полученный файл, натыкается на первый же трейлер оригинального (первого) документа, считывает его собственную таблицу xref и считает, что документ на этом завершен. Все данные второго документа, расположенные дальше по байтовому смещению, ридером игнорируются, хотя физически они присутствуют в файле (что и объясняет увеличившийся размер файла).

Следовательно, для корректного слияния нам необходимо использовать инструменты, которые умеют перестраивать внутреннюю логическую структуру PDF, объединять таблицы перекрестных ссылок и формировать единый корректный трейлер.

Способ 1. Использование встроенных механизмов платформы 1С:Предприятие 8.3

Если объединяемые документы формируются непосредственно внутри вашей конфигурации 1С в виде табличных документов (объекты типа ТабличныйДокумент), то лучшим решением будет отказаться от промежуточного сохранения отдельных файлов PDF. Платформа 1С:Предприятие предоставляет встроенный инструмент для пакетной работы с печатными формами — объект ПакетОтображаемыхДокументов.

Проанализируем пример кода, который позволяет собрать несколько табличных документов в один многостраничный PDF-файл без привлечения сторонних утилит:


// Создаем массив табличных документов, которые необходимо объединить
МассивПечатныхФорм = Новый Массив;
МассивПечатныхФорм.Добавить(ТабличныйДокументСчет);
МассивПечатныхФорм.Добавить(ТабличныйДокументНакладная);

// Инициализируем пакет отображаемых документов
ПакетДокументов = Новый ПакетОтображаемыхДокументов;
ПакетДокументов.ФорматИмениФайлаПечати = ФорматИмениФайлаПакетногоДокумента.Индивидуально;

Для Каждого ТекущийДок Из МассивПечатныхФорм Цикл
    // Помещаем каждый табличный документ во временное хранилище
    АдресВременногоХранилища = ПоместитьВоВременноеХранилище(ТекущийДок);
    
    // Добавляем элемент в состав пакета
    ЭлементПакета = ПакетДокументов.Состав.Добавить();
    ЭлементПакета.Данные = АдресВременногоХранилища;
КонецЦикла;

// Указываем путь для сохранения результирующего многостраничного PDF
ПутьКРезультирующемуФайлу = "C:\Temp\CompleteSet.pdf";
ПакетДокументов.ЗаписатьФайлДляПечати(ПутьКРезультирующемуФайлу, ТипФайлаПакетногоДокумента.PDF);

Этот способ является максимально надежным, так как он работает «из коробки», полностью кроссплатформенен (работает как под управлением Windows, так и под Linux) и гарантирует формирование валидного PDF-документа.

Способ 2. Объединение готовых файлов с помощью консольной утилиты PDFtk

В случаях, когда файлы PDF уже лежат на диске (например, они были загружены пользователями или получены из внешних систем), нам потребуется использовать специализированные внешние утилиты. Одной из самых популярных, быстрых и надежных программ для этой задачи является кроссплатформенная консольная утилита PDFtk (PDF Toolkit).

Для серверных систем Windows или Linux достаточно скачать исполняемый файл утилиты. В пакетном (консольном) режиме данный инструмент полностью бесплатен и отлично подходит для вызова из кода 1С через метод ЗапуститьПриложение().

Рассмотрим по шагам, как реализовать интеграцию с PDFtk в коде 1С:


// Путь к исполняемому файлу PDFtk на сервере
ПутьКПриложению = "C:\Program Files (x86)\PDFtk\bin\pdftk.exe";

// Формируем имена исходных файлов и результирующего документа
Файл1 = "C:\Temp\Invoice.pdf";
Файл2 = "C:\Temp\Specification.pdf";
ФайлРезультат = "C:\Temp\Merged_PDFtk.pdf";

// Формируем параметры командной строки. 
// Шаблон команды: pdftk file1.pdf file2.pdf cat output result.pdf
ПараметрыЗапуска = СтрШаблон("""%1"" ""%2"" cat output ""%3""", Файл1, Файл2, ФайлРезультат);

// Запуск приложения в синхронном режиме (ожидаем завершения склейки)
КодВозврата = Неопределено;
ЗапуститьПриложение(ПутьКПриложению + " " + ПараметрыЗапуска, , Истина, КодВозврата);

Если КодВозврата = 0 Тогда
    ЗаписьЖурналаРегистрации("Слияние PDF", УровеньЖурналаРегистрации.Информация, , , "Файлы успешно объединены.");
Иначе
    ВызватьИсключение "Ошибка при объединении PDF-файлов с помощью PDFtk. Код возврата: " + КодВозврата;
КонецЕсли;

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

Способ 3. Использование Ghostscript

Еще один мощный профессиональный инструмент для работы с PostScript и PDF — утилита Ghostscript. Она часто предустановлена на Linux-серверах и легко разворачивается на ОС Windows.

Слияние файлов происходит за счет пересборки PDF виртуальным принтером Ghostscript. Посмотрим на пример вызова утилиты:


// Укажем имя исполняемого файла Ghostscript (например, gswin64c.exe для Windows x64)
ИсполняемыйФайл = "gswin64c.exe";

Файл1 = "C:\Temp\Page1.pdf";
Файл2 = "C:\Temp\Page2.pdf";
ВыходнойФайл = "C:\Temp\Result_GS.pdf";

// Формируем строку аргументов для тихой работы без вывода диалоговых окон
// -dNOPAUSE - отключить паузы между страницами
// -sDEVICE=pdfwrite - использовать конвертер в PDF
// -sOUTPUTFILE - путь к новому файлу
// -dBATCH - завершить работу после обработки всех аргументов
СтрокаПараметров = СтрШаблон("-dNOPAUSE -sDEVICE=pdfwrite -sOUTPUTFILE=""%1"" -dBATCH ""%2"" ""%3""", 
                             ВыходнойФайл, Файл1, Файл2);

ЗапуститьПриложение(ИсполняемыйФайл + " " + СтрокаПараметров, , Истина);

Способ 4. Современная альтернатива для Linux-серверов: pdfunite

Если сервер вашего приложения 1С работает под управлением операционной системы Linux (Ubuntu, Debian, RedHat), наиболее лаконичным и современным решением будет использование стандартной утилиты pdfunite, которая входит в популярный пакет poppler-utils. Она устанавливается стандартным пакетным менеджером (например, sudo apt-get install poppler-utils).

Команда вызова из 1С предельно проста и не требует сложных аргументов настроек:


КомандаЗапуска = "pdfunite /tmp/doc1.pdf /tmp/doc2.pdf /tmp/merged_linux.pdf";
ЗапуститьПриложение(КомандаЗапуска, , Истина);

Использование pdfunite гарантирует быстрое слияние без лишней нагрузки на серверную часть и исключает необходимость разворачивать громоздкие Java или .NET-зависимые библиотеки.

Способ 5. Программные библиотеки COM/ActiveX (на примере Debenu Quick PDF Library)

Если ваша инфраструктура ориентирована исключительно на Windows-окружение и вы предпочитаете управлять процессом слияния объектов внутри кода 1С через COM-соединение, можно использовать специализированные библиотеки. Одной из таких библиотек является Debenu Quick PDF Library (доступна в том числе бесплатная Lite-версия).

Работа с COM-объектом позволяет осуществлять тонкую настройку слияния, добавлять водяные знаки, штампы или извлекать метаданные файлов «на лету». Общая схема работы выглядит следующим образом:


Попытка
    PDFБиблиотека = Новый COMОбъект("DebenuPDFLibraryLite.PDFLibrary");
Исключение
    Предупреждение("Компонент Debenu Quick PDF Library не зарегистрирован на компьютере!");
    Возврат;
КонецПопытки;

// Загружаем первый документ в память
Если PDFБиблиотека.LoadFromFile("C:\Temp\First.pdf", "") = 1 Тогда
    // Получаем внутренний идентификатор первого документа
    ИдентификаторПервого = PDFБиблиотека.SelectedDocument();
    
    // Загружаем второй документ
    Если PDFБиблиотека.LoadFromFile("C:\Temp\Second.pdf", "") = 1 Тогда
        ИдентификаторВторого = PDFБиблиотека.SelectedDocument();
        
        // Переключаемся на первый документ и осуществляем программное слияние
        PDFБиблиотека.SelectDocument(ИдентификаторПервого);
        PDFБиблиотека.MergeDocument(ИдентификаторВторого);
        
        // Сохраняем объединенный файл
        PDFБиблиотека.SaveToFile("C:\Temp\Merged_Debenu.pdf");
    КонецЕсли;
КонецЕсли;

Подводя итог, можно сделать вывод: если вы формируете отчеты самостоятельно в 1С, всегда используйте штатный ПакетОтображаемыхДокументов. Если же вам приходится оперировать сторонними файлами на диске, наилучшим и наиболее отказоустойчивым выбором станет вызов консольной утилиты PDFtk для Windows-инфраструктуры или pdfunite для систем под управлением Linux.

← На главную