В процессе автоматизации документооборота, сканирования или обработки архивных документов программисты часто сталкиваются с задачей объединения нескольких графических файлов формата TIFF в один единый многостраничный документ. Формат TIFF (Tagged Image File Format) уникален тем, что, в отличие от JPEG или PNG, он изначально поддерживает многостраничность. Это делает его крайне удобным для хранения сканов договоров, первичных документов и бухгалтерской отчетности.
В этой статье мы подробно разберем, как решить задачу объединения таких файлов в среде 1С:Предприятие. Мы проанализируем популярные решения с использованием внешних утилит, укажем на критические ошибки производительности, которые часто совершают разработчики, и рассмотрим современный нативный способ решения этой задачи без привлечения стороннего программного обеспечения.
Рассмотрим стандартную ситуацию, с которой столкнулся автор темы на форуме. Имеется каталог с набором одностраничных файлов TIFF (например, полученных с потокового сканера), имена которых начинаются с префикса Вх. Задача состоит в том, чтобы собрать их все в один файл Результат.tiff.
В ходе обсуждения участники пришли к следующему коду для последовательного объединения файлов с помощью консольной утилиты ImageMagick:
Первая = Истина;
Для каждого Стр1 Из НайденныеФайлы Цикл
Нстр = ТЧФайлов.Добавить();
Нстр.ФайлTIFF = Стр1.Имя;
Если Первая = Истина Тогда
ЗапускОбъединения = (КомандаПрограммы + " " + Стр1.Имя + " " + ИмяФайла);
ЗапуститьПриложение(ЗапускОбъединения, ИмяКаталога, Истина, КодВозврата);
Первая = Ложь;
Продолжить;
КонецЕсли;
// Внимание: в этой строке на форуме содержится синтаксическая ошибка!
ЗапускОбъединения = (КомандаПрограммы + " " + ИмяФайла + " " + Стр.ИмяФайла + " " + ИмяФайла);
ЗапуститьПриложение(ЗапускОбъединения, ИмяКаталога, Истина, КодВозврата);
КонецЦикла;
Давайте подробно проанализируем этот код и выясним, почему использовать его в реальных рабочих базах категорически не рекомендуется.
Во-первых, в коде присутствует явная опечатка. В строке формирования команды используется переменная Стр и обращение к свойству ИмяФайла, хотя в цикле обход идет по переменной Стр1, а имя файла хранится в свойстве Имя или ПолноеИмя системного объекта Файл. При попытке выполнить этот код система 1С выдаст ошибку времени выполнения (Runtime Error).
Во-вторых, и это самое главное, предложенный алгоритм страдает катастрофическим падением производительности при росте количества объединяемых файлов. Давайте разберем по шагам, что происходит на диске при каждой итерации цикла:
Результат.tiff, содержащий первую страницу.convert.exe считывает с диска Результат.tiff (1 страница), считывает второй файл, декодирует их в оперативной памяти, объединяет и записывает обратно на диск файл Результат.tiff (уже 2 страницы).Результат.tiff (в котором уже накоплено 99 страниц), полностью декодирует его структуру, считывает сотый файл, производит объединение и записывает на диск новый файл объемом в 100 страниц.В результате мы получаем квадратичную сложность алгоритма по дисковым операциям записи. При обработке большого пакета документов (например, 200–300 листов с высоким разрешением) сервер 1С начнет испытывать колоссальную нагрузку на дисковую подсистему, а сам процесс объединения может затянуться на долгие минуты, вызывая зависание сеанса пользователя или фонового задания.
Как совершенно справедливо заметил один из участников форума, внешняя утилита ImageMagick способна самостоятельно работать с масками файлов и выполнять склейку за один проход, без необходимости поочередного перебора файлов в цикле 1С.
Рассмотрим, как правильно составить команду для консоли. Вместо того чтобы вызывать утилиту N раз, мы можем передать ей маску файлов и путь к результирующему файлу. Утилита сама найдет все подходящие исходники, считает их в память в один поток и запишет готовый многостраничный TIFF на диск ровно один раз.
Обратите внимание, что синтаксис зависит от используемой версии ImageMagick:
convert.exe.convert интегрирована в единое консольное приложение, и запуск осуществляется через magick.exe.Ниже представлен правильный и эффективный шаблон кода 1С для вызова утилиты ImageMagick за один проход:
ИмяКаталога = "D:\Temp\2020\";
МаскаФайлов = "Вх*.tiff";
ИмяРезультата = "Результат.tiff";
ПутьКПрограмме = "C:\Program Files\ImageMagick-7.1.0-Q16-HDRI\magick.exe";
// Формируем параметры запуска. Все пути к файлам обязательно оборачиваем в двойные кавычки,
// чтобы избежать проблем, если в путях к папкам будут содержаться пробелы.
ПараметрыЗапуска = """" + ИмяКаталога + МаскаФайлов + """ """ + ИмяКаталога + ИмяРезультата + """";
ПолнаяКоманда = """" + ПутьКПрограмме + """ " + ПараметрыЗапуска;
КодВозврата = 0;
ЗапуститьПриложение(ПолнаяКоманда, ИмяКаталога, Истина, КодВозврата);
Если КодВозврата = 0 Тогда
Сообщить("Объединение файлов успешно завершено!");
Иначе
Сообщить("При объединении файлов возникла ошибка. Код возврата: " + Строка(КодВозврата));
КонецЕсли;
Этот метод работает в сотни раз быстрее циклического перебора, так как дисковые операции сводятся к минимуму. Однако у него остается один существенный недостаток: необходимость установки стороннего ПО (ImageMagick) на каждый рабочий компьютер пользователя или на сервер приложений 1С, а также настройка прав операционной системы на запуск исполняемых файлов из кода 1С.
Начиная с версии платформы 1С:Предприятие 8.3.15, разработчики получили мощный инструмент для работы с изображениями — объект ОбрабатываемаяКартинка. Этот объект позволяет выполнять базовые операции с графикой (изменение размера, поворот, конвертация форматов) непосредственно в коде 1С без использования сторонних внешних компонент или консольных утилит.
С помощью ОбрабатываемаяКартинка мы можем собрать многостраничный TIFF полностью в оперативной памяти сервера или клиента. Посмотрим, как это реализовать на практике.
Разберем готовый программный код:
// Определяем исходные параметры
КаталогИсточника = "D:\Temp\2020";
МаскаПоиска = "Вх*.tiff";
ПутьКРезультату = "D:\Temp\2020\Результат.tiff";
// Шаг 1. Находим все файлы по маске
НайденныеФайлы = НайтиФайлы(КаталогИсточника, МаскаПоиска, Ложь);
Если НайденныеФайлы.Количество() = 0 Тогда
ВызватьИсключение "Не найдено ни одного файла для объединения по маске " + МаскаПоиска;
КонецЕсли;
// Шаг 2. Заполняем массив объектами "Картинка"
МассивКартинок = Новый Массив;
Для каждого Файл Из НайденныеФайлы Цикл
// Считываем файл с диска как объект Картинка и добавляем в массив
ТекущаяКартинка = Новый Картинка(Файл.ПолноеИмя);
МассивКартинок.Добавить(ТекущаяКартинка);
КонецЦикла;
// Шаг 3. Инициализируем объект "ОбрабатываемаяКартинка"
// Передаем в конструктор массив наших картинок и указываем целевой формат TIFF
Обрабатываемая = Новый ОбрабатываемаяКартинка(МассивКартинок, ФорматКартинки.TIFF);
// Шаг 4. Получаем результирующее многостраничное изображение
РезультирующаяКартинка = Обрабатываемая.ПолучитьКартинку();
// Шаг 5. Записываем файл на диск
РезультирующаяКартинка.Записать(ПутьКРезультату);
Сообщить("Успешно объединено файлов: " + Строка(МассивКартинок.Количество()));
Рассмотрим подробнее преимущества этого подхода:
.exe.ЗапуститьПриложение часто отключается системными администраторами из соображений безопасности. Нативный код 1С работает в рамках штатных механизмов платформы.Если ваша информационная база по-прежнему работает на платформе версии ниже 8.3.15, а устанавливать тяжелый графический пакет ImageMagick (который весит более 100 МБ в установленном виде) нет возможности, хорошей альтернативой будет использование специализированной утилиты tiffcp.
Утилита tiffcp входит в состав официального свободно распространяемого набора библиотек LibTIFF. Она весит менее 1 мегабайта и предназначена исключительно для копирования и конкатенации (объединения) TIFF-файлов.
Для объединения файлов с ее помощью используется следующий синтаксис запуска:
// Специфика синтаксиса tiffcp позволяет перечислить все исходные файлы через пробел,
// а в конце указать имя результирующего файла.
ЗапускКоманды = "D:\LibTIFF\tiffcp.exe D:\Temp\2020\Вх*.tiff D:\Temp\2020\Результат.tiff";
ЗапуститьПриложение(ЗапускКоманды, , Истина);
Если же вам требуется программный отбор файлов в коде 1С перед их склеиванием, вы можете использовать параметр добавления -a (append). В отличие от ImageMagick, утилита tiffcp -a не перекодирует файл заново при добавлении страницы, а просто физически дописывает структуру новой страницы в конец существующего файла. Это делает циклическое добавление страниц через tiffcp -a очень быстрым и безопасным для дисков процессом.
Подводя итоги, при решении задачи объединения графических документов в 1С рекомендуем придерживаться следующих правил:
ОбрабатываемаяКартинка. Это наиболее надежный, безопасный и современный способ, который избавит вас от проблем при миграции сервера на Linux или при обновлении операционных систем.tiffcp, если вам не требуется сложная обработка изображений (изменение цветовой палитры, наложение фильтров и т.д.), которую предоставляет ImageMagick.