Как решить проблему с отображением PDF в поле HTML-документа в 1С после обновления платформы?

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

При обновлении платформы 1С:Предприятие до современных релизов (начиная с версии 8.3.14 и выше, включая ветки 8.3.16, 8.3.20 и т.д.) разработчики часто сталкиваются с неприятным сюрпризом: в элементах управляемой формы с типом ПолеHTMLДокумента внезапно перестают отображаться файлы PDF, загружаемые с диска или внешних интернет-ресурсов. Вместо привычного документа пользователь видит либо пустой белый экран, либо серую заглушку, либо черный квадрат.

В этой статье мы подробно разберем, почему произошли эти изменения, как вернуть отображение документов с помощью обходных путей на более старых версиях платформы и как реализовать это красиво и штатно, если у вас есть возможность использовать современные релизы 1С.

Выясняем причину: великий переход на WebKit

Прежде чем приступать к написанию кода, давайте проанализируем ситуацию и поймем физику процесса. В старых релизах платформы (до версии 8.3.14) элемент формы ПолеHTMLДокумента в операционной системе Windows базировался на технологиях Microsoft. Фактически внутри формы запускался COM-объект MSHTML (движок браузера Internet Explorer). Когда в такой HTML-контейнер передавался тег <embed type="application/pdf"> или <iframe> с путем к PDF, операционная система автоматически подключала внешние зарегистрированные плагины (например, Adobe Acrobat Reader, Foxit Reader или PDF-XChange Viewer через механизмы ActiveX).

Начиная со сборки 8.3.14.1565, фирма «1С» перевела клиентские приложения Windows на использование кроссплатформенного движка WebKit для рендеринга HTML-содержимого. Это было сделано для унификации работы интерфейса программы на различных операционных системах (Windows, Linux, macOS) и ускорения работы отрисовки форм. Новый встроенный движок WebKit по соображениям безопасности и кроссплатформенности не поддерживает работу устаревших плагинов ActiveX. Как результат — старый HTML-код, отвечающий за вывод PDF-файлов напрямую, перестал работать.

Решение №1: Использование библиотеки PDF.js (для версий платформы от 8.3.14 до 8.3.21)

Если ваша база данных работает на версиях платформы младше 8.3.22, оптимальным решением внутри управляемой формы является использование открытой JavaScript-библиотеки от компании Mozilla — PDF.js. Этот скрипт умеет декодировать структуру PDF и отрисовывать страницы документа прямо внутри HTML-контейнера на стандартном элементе <canvas> силами самого браузерного движка.

Для того чтобы решение работало стабильно и не зависело от наличия интернет-соединения у конечного пользователя, мы откажемся от внешних CDN-ссылок и внедрим JS-библиотеку непосредственно в конфигурацию 1С. Разберем этот процесс по шагам:

  1. Скачайте стабильную сборку библиотеки PDF.js (вам понадобятся файлы pdf.js и pdf.worker.js).
  2. В дереве метаданных вашей конфигурации перейдите в ветку Общие макеты и создайте два макета с типом «Текстовый документ» или «HTML-документ». Назовите их, например, МакетБиблиотекиPDF_JS и МакетБиблиотекиPDF_Worker. Скопируйте туда содержимое скачанных скриптов.
  3. На управляемой форме добавьте реквизит с типом «Строка» (например, АдресHTMLТела) и свяжите его с элементом формы ПолеHTMLДокумента.

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

Применим следующий код на языке 1С для генерации HTML-страницы:


&НаСервере
Функция СформироватьHTMLДляПросмотраPDF(ПутьКФайлу) Экспорт
    
    // Шаг 1. Читаем PDF-файл в двоичные данные и конвертируем в Base64
    Попытка
        ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПутьКФайлу);
        СтрокаBase64 = ПолучитьBase64СтрокуИзДвоичныхДанных(ДвоичныеДанныеФайла);
    Исключение
        ВызватьИсключение "Не удалось прочитать файл PDF: " + ОписаниеОшибки();
    КонецПопытки;
    
    // Шаг 2. Получаем тексты библиотек из макетов конфигурации
    КодБиблиотекиJS = ПолучитьОбщийМакет("МакетБиблиотекиPDF_JS").ПолучитьТекст();
    КодВоркераJS = ПолучитьОбщийМакет("МакетБиблиотекиPDF_Worker").ПолучитьТекст();
    
    // Шаг 3. Собираем HTML-контейнер
    ТекстHTML = 
    "<!DOCTYPE html>
    |<html>
    |<head>
    |   <meta charset=""utf-8"">
    |   <style>
    |       body { margin: 0; padding: 0; background-color: #404040; }
    |       #canvas-container { text-align: center; padding: 10px; }
    |       canvas { display: block; margin: 10px auto; box-shadow: 0 4px 8px rgba(0,0,0,0.2); }
    |   </style>
    |   <script>" + КодБиблиотекиJS + "</script>
    |</head>
    |<body>
    |   <div id=""canvas-container""></div>
    |
    |   <script>
    |       // Настройка фонового воркера напрямую из строки кода
    |       var workerBlob = new Blob([""" + СтрЗаменить(КодВоркераJS, """", "\""") + """], {type: 'application/javascript'});
    |       var workerURL = URL.createObjectURL(workerBlob);
    |       pdfjsLib.GlobalWorkerOptions.workerSrc = workerURL;
    |
    |       // Декодируем Base64 строку с PDF
    |       var pdfData = atob('" + СтрокаBase64 + "');
    |
    |       // Загружаем документ
    |       var loadingTask = pdfjsLib.getDocument({data: pdfData});
    |       loadingTask.promise.then(function(pdf) {
    |           var container = document.getElementById('canvas-container');
    |           
    |           // Постранично рендерим PDF на холсты canvas
    |           for (var pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
    |               pdf.getPage(pageNum).then(function(page) {
    |                   var scale = 1.5;
    |                   var viewport = page.getViewport({scale: scale});
    |
    |                   var canvas = document.createElement('canvas');
    |                   var context = canvas.getContext('2d');
    |                   canvas.height = viewport.height;
    |                   canvas.width = viewport.width;
    |                   container.appendChild(canvas);
    |
    |                   var renderContext = {
    |                       canvasContext: context,
    |                       viewport: viewport
    |                   };
    |                   page.render(renderContext);
    |               });
    |           }
    |       });
    |   </script>
    |</body>
    |</html>";
    
    Возврат ТекстHTML;
    
КонецФункции

Этот метод полностью автономен, не требует сторонних программ на клиентском ПК и возвращает готовую строку HTML, которую достаточно присвоить реквизиту формы. Однако помните, что при работе с большими многостраничными документами (более 50 листов) рендеринг через виртуальный холст JS может потреблять значительный объем оперативной памяти клиентского компьютера.

Решение №2: Конвертация PDF в графический формат на стороне сервера

Рассмотрим альтернативный вариант, который отлично подходит для высоконагруженных систем или сред со слабыми клиентскими машинами. Вместо рендеринга тяжелого PDF силами JS на клиенте, мы можем делегировать эту задачу серверу приложений. Сервер конвертирует страницы PDF в оптимизированные изображения (например, JPEG) и отдает клиенту легкий HTML со стандартными тегами <img>. Для этой задачи можно использовать готовое решение для программной конвертации PDF в формат PNG.

Для реализации конвертации можно использовать проверенные бесплатные консольные утилиты, такие как Ghostscript или MuPDF (в частности, инструмент mutool). Посмотрим на пример вызова внешней утилиты через встроенный язык 1С:


&НаСервере
Процедура КонвертироватьPDFВИзображения(ПутьКФайлуPDF, КаталогРезультата)
    
    // Задаем параметры запуска для утилиты MuPDF (команда draw преобразует PDF в изображения)
    ИмяИсполняемогоФайла = "C:\mupdf\mutool.exe";
    ШаблонВывода = КаталогРезультата + "\page-%d.png";
    ПараметрыЗапуска = "draw -o """ + ШаблонВывода + """ """ + ПутьКФайлуPDF + """";
    
    // Запускаем процесс конвертации в синхронном режиме
    КодВозврата = Неопределено;
    ЗапуститьПриложение(ИмяИсполняемогоФайла + " " + ПараметрыЗапуска, , Истина, КодВозврата);
    
    Если КодВозврата <> 0 Тогда
        ВызватьИсключение "Ошибка конвертации PDF утилитой MuPDF. Код ошибки: " + Строка(КодВозврата);
    КонецЕсли;
    
КонецПроцедуры

Чтобы избежать повторной конвертации при каждом открытии формы одним и тем же пользователем, рекомендуется реализовать кэширование на стороне сервера 1С:

Решение №3: Современный штатный подход в платформе 1С (начиная с версии 8.3.22)

Если вы имеете возможность обновить технологическую платформу 1С до версии 8.3.22 или выше, вам больше не нужно прибегать к неофициальным библиотекам и внешним консольным утилитам. Разработчики фирмы «1С» услышали боль сообщества и добавили полноценную встроенную поддержку отображения PDF.

Проанализируем новые штатные возможности, которые предоставляет современная платформа:

  1. Новый тип элемента формы — «Поле PDF-документа»: Теперь при редактировании управляемой формы в конфигураторе вы можете добавить стандартный элемент управления с типом ПолеPDFДокумента. В качестве пути к данным указывается реквизит формы, содержащий двоичные данные или путь к файлу. Платформа отобразит документ с использованием встроенного быстрого просмотрщика.
  2. Объект «ДокументPDF» во встроенном языке: Появился полноценный программный интерфейс для работы с этим форматом. Вы можете открыть файл PDF интерактивно из кода одной командой.

Рассмотрим пример простого вызова просмотра документа на клиенте с помощью нового объекта платформы:


&НаКлиенте
Процедура ПросмотретьДокументНативно(ПутьКФайлу)
    
    // Создаем объект ДокументPDF на клиенте и открываем окно просмотра
    ДокументПросмотра = Новый ДокументPDF;
    ДокументПросмотра.Прочитать(ПутьКФайлу);
    ДокументПросмотра.Показать("Просмотр печатной формы документа");
    
КонецПроцедуры

В открывшемся системном окне пользователю сразу будет доступна удобная панель инструментов со следующими возможностями:

Сводная таблица методов решения проблемы

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

← На главную