При автоматизации документооборота в 1С часто возникает задача объединения нескольких сгенерированных PDF-файлов (например, расходной накладной, счета и сертификатов соответствия) в один многостраничный документ для последующей отправки клиенту по электронной почте. Попытка решить эту задачу «в лоб» с помощью стандартного метода платформы ОбъединитьФайлы() или бинарного сложения через командную строку Windows (команда copy /b) приводит к неожиданному результату: размер итогового файла увеличивается, но при открытии отображается только первая страница или первый документ. В этой статье мы подробно разберем, почему так происходит, и рассмотрим рабочие способы корректной склейки PDF-документов.
Выясним причину, по которой обычное слияние байтов оказывается неэффективным для формата PDF. Дело в том, что PDF (Portable Document Format) — это не просто последовательный поток текстовых или графических данных, а сложный структурированный объектный файл. Его структура включает в себя следующие обязательные элементы:
xref, а также содержит уникальный идентификатор документа и ссылку на корневой объект.Программы для чтения PDF (такие как Adobe Acrobat Reader, Foxit Reader или встроенные просмотрщики в веб-браузерах) всегда начинают обработку файла с самого конца. Они ищут маркер конца файла (EOF), считывают трейлер, находят таблицу xref и по ней строят дерево объектов.
Когда мы пытаемся применить метод ОбъединитьФайлы() или команду copy /b file1.pdf + file2.pdf result.pdf, второй файл просто дописывается в самый хвост первого. Программа-ридер открывает полученный файл, натыкается на первый же трейлер оригинального (первого) документа, считывает его собственную таблицу xref и считает, что документ на этом завершен. Все данные второго документа, расположенные дальше по байтовому смещению, ридером игнорируются, хотя физически они присутствуют в файле (что и объясняет увеличившийся размер файла).
Следовательно, для корректного слияния нам необходимо использовать инструменты, которые умеют перестраивать внутреннюю логическую структуру PDF, объединять таблицы перекрестных ссылок и формировать единый корректный трейлер.
Если объединяемые документы формируются непосредственно внутри вашей конфигурации 1С в виде табличных документов (объекты типа ТабличныйДокумент), то лучшим решением будет отказаться от промежуточного сохранения отдельных файлов PDF. Платформа 1С:Предприятие предоставляет встроенный инструмент для пакетной работы с печатными формами — объект ПакетОтображаемыхДокументов.
Проанализируем пример кода, который позволяет собрать несколько табличных документов в один многостраничный PDF-файл без привлечения сторонних утилит:
// Создаем массив табличных документов, которые необходимо объединить
МассивПечатныхФорм = Новый Массив;
МассивПечатныхФорм.Добавить(ТабличныйДокументСчет);
МассивПечатныхФорм.Добавить(ТабличныйДокументНакладная);
// Инициализируем пакет отображаемых документов
ПакетДокументов = Новый ПакетОтображаемыхДокументов;
ПакетДокументов.ФорматИмениФайлаПечати = ФорматИмениФайлаПакетногоДокумента.Индивидуально;
Для Каждого ТекущийДок Из МассивПечатныхФорм Цикл
// Помещаем каждый табличный документ во временное хранилище
АдресВременногоХранилища = ПоместитьВоВременноеХранилище(ТекущийДок);
// Добавляем элемент в состав пакета
ЭлементПакета = ПакетДокументов.Состав.Добавить();
ЭлементПакета.Данные = АдресВременногоХранилища;
КонецЦикла;
// Указываем путь для сохранения результирующего многостраничного PDF
ПутьКРезультирующемуФайлу = "C:\Temp\CompleteSet.pdf";
ПакетДокументов.ЗаписатьФайлДляПечати(ПутьКРезультирующемуФайлу, ТипФайлаПакетногоДокумента.PDF);
Этот способ является максимально надежным, так как он работает «из коробки», полностью кроссплатформенен (работает как под управлением Windows, так и под Linux) и гарантирует формирование валидного PDF-документа.
В случаях, когда файлы 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. Код возврата: " + КодВозврата;
КонецЕсли;
Преимущество этого метода заключается в высочайшей скорости работы и низком потреблении оперативной памяти сервера, что критично при обработке больших пакетов документов (например, сотен сертификатов за один сеанс отгрузки).
Еще один мощный профессиональный инструмент для работы с 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);
ЗапуститьПриложение(ИсполняемыйФайл + " " + СтрокаПараметров, , Истина);
Если сервер вашего приложения 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-зависимые библиотеки.
Если ваша инфраструктура ориентирована исключительно на 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.