При разработке внешних печатных форм или кастомных отчетов в конфигурации 1С:Зарплата и управление персоналом 3.1 часто возникает задача визуализации данных, а именно — вывода фотографии сотрудника. На первый взгляд задача кажется простой, но разработчики часто сталкиваются с ошибками типа Несоответствие типов или ситуациями, когда область для картинки остается пустой. Рассмотрим подробно, как правильно извлечь изображение из базы данных и корректно отобразить его в табличном документе.
Прежде чем приступать к написанию кода, проанализируем, где и в каком виде хранятся фотографии в 1С. В типовом решении ЗУП 3.1 для этого используется регистр сведений ФотографииФизическихЛиц. Этот регистр имеет одно ключевое измерение — ФизическоеЛицо и ресурс Фотография с типом ХранилищеЗначений.
Важно понимать: данные в ресурсе Фотография хранятся не в виде готового объекта Картинка, а в упакованном виде внутри объекта ХранилищеЗначений. Это необходимо для оптимизации базы данных, но требует от программиста дополнительных действий по «распаковке» данных.
Рассмотрим классический подход, который часто используют начинающие разработчики. Он подходит для вывода фото в единичную печатную форму (например, пропуск сотрудника).
Разберем по шагам алгоритм получения данных:
Получить().Посмотрим на пример кода, который учитывает возможные ошибки типизации:
// Предположим, что ФизическоеЛицоСсылка — это ссылка на справочник ФизическиеЛица
Запись = РегистрыСведений.ФотографииФизическихЛиц.СоздатьМенеджерЗаписи();
Запись.ФизическоеЛицо = ФизическоеЛицоСсылка;
Запись.Прочитать();
Если Запись.Выбран() Тогда
// Получаем содержимое хранилища значений
ДанныеФото = Запись.Фотография.Получить();
// Проверяем, не пустое ли хранилище
Если ДанныеФото <> Неопределено Тогда
// Проверяем тип данных: там может быть уже объект Картинка или ДвоичныеДанные
Если ТипЗнч(ДанныеФото) = Тип("Картинка") Тогда
ОбъектКартинка = ДанныеФото;
Иначе
// Если там двоичные данные, создаем из них объект Картинка
ОбъектКартинка = Новый Картинка(ДанныеФото);
КонецЕсли;
КонецЕсли;
КонецЕсли;
Теперь, когда у нас есть объект ОбъектКартинка, нам нужно поместить его в макет. Рассмотрим ситуацию, когда мы программно добавляем рисунок в область макета.
Проанализируем процесс настройки свойств рисунка:
// Получаем область из макета
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
// Добавляем новый рисунок типа "Картинка"
Рисунок = ОбластьЗаголовок.Рисунки.Добавить(ТипРисункаТабличногоДокумента.Картинка);
// Устанавливаем координаты и размеры (в миллиметрах)
Рисунок.Верх = 5;
Рисунок.Высота = 40;
Рисунок.Ширина = 30;
Рисунок.Лево = 150;
// Настраиваем поведение картинки внутри рамки
Рисунок.РазмерКартинки = РазмерКартинки.Пропорционально;
// Присваиваем саму картинку
Если ОбъектКартинка <> Неопределено Тогда
Рисунок.Картинка = ОбъектКартинка;
КонецЕсли;
// Выводим область в результирующий табличный документ
ТабДок.Вывести(ОбластьЗаголовок);
Использование РазмерКартинки.Пропорционально крайне важно. Если установить АвтоРазмер, фотография может исказить границы ячеек макета, а РеальныйРазмер может отобразить лишь фрагмент изображения, если исходный файл имеет высокое разрешение.
Если вам нужно вывести фотографии для списка из 100 сотрудников, использование СоздатьМенеджерЗаписи() в цикле будет крайне неэффективным, так как это порождает 100 отдельных обращений к серверу БД. В этом случае выясним причину медленной работы и заменим её на пакетный запрос.
Рассмотрим пример запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Фотографии.Фотография КАК Фотография,
| Сотрудники.Ссылка КАК Сотрудник
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ФотографииФизическихЛиц КАК Фотографии
| ПО Сотрудники.ФизическоеЛицо = Фотографии.ФизическоеЛицо
|ГДЕ
| Сотрудники.Ссылка В(&МассивСотрудников)";
Запрос.УстановитьПараметр("МассивСотрудников", СписокСсылок);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Работаем с данными ХранилищаЗначений из результата запроса
Если ЗначениеЗаполнено(Выборка.Фотография) Тогда
КартинкаИзБД = Выборка.Фотография.Получить();
// Далее код вывода, аналогичный предыдущему примеру
КонецЕсли;
КонецЦикла;
При работе с изображениями часто возникают нюансы, которые могут поставить в тупик:
ХранилищеЗначений свойству Картинка. Всегда используйте метод .Получить().ФизическиеЛица (дубли). Также убедитесь, что вызов ТабДок.Вывести() происходит после того, как в область был добавлен рисунок.Получить() может вернуть Неопределено, если файл в базе поврежден или отсутствует. Добавьте проверку Если ТипЗнч(Данные) = Тип("ДвоичныеДанные").В современных конфигурациях 1С (на базе БСП) существуют общие модули, упрощающие работу. Проанализируем ситуацию: если в вашей конфигурации есть модуль ФизическиеЛицаКлиентСервер или УправлениеСлужебнымиДанными, поищите в них функции получения фотографий. Зачастую платформа уже имеет готовый механизм, который сам проверяет наличие данных и возвращает пустую заглушку «Нет фото», если данных нет, что выглядит гораздо профессиональнее в готовом отчете.
Совет: При работе с большим количеством фотографий не забывайте об очистке памяти. После вывода каждого рисунка в табличный документ можно явно обнулять переменную, хранящую объект картинки (ОбъектКартинка = Неопределено;), чтобы сборщик мусора быстрее освобождал оперативную память сервера.