Непослушный PDF
На работе встал вопрос периодически (по регламентному заданию) сохранять в pdf-файл отчет из табличного документа. Задача осложнялась тем, что помимо стандартных текста, чисел, таблиц, готовый отчет должен содержать картинку — логотип предприятия.
Механизм платформы
Сначала решить задачу попытались наиболее простым и логичным способом — типовой экспорт табличного документа. Всё бы ничего, но вот в Adobe Reader полученный документ не открывается (ошибка 135). Исследование в hex-редакторе показало, что платформа «ломает» теги шрифта в готовом файле. Эта проблема известна фирме 1С и уже была исправлена (проверено) в релизе 1С:Предприятие 8.3.8, который выйдет предположительно весной сего года. Интересно, что платформа 8.2 такой особенностью не обладает и сохраняет pdf корректно.
ТабличныйДокумент.Записать(ПутьДляСохранения, ТипФайлаТабличногоДокумента.PDF);
PDF Creator и виртуальные принтеры
Невозможность решить проблему типовыми средствами вынудила искать возможности в сторонних средствах. Первое такое решение — воспользоваться виртуальным принтером в pdf. На рынке есть программа PDFCreator, позволяющая выполнять свою главную функцию, «печать» в pdf, бесплатно. Несмотря на кажущуюся сложность, настройка программы элементарна — нужно лишь указать путь сохранения готового файла. После чего, направить исходный файл на виртуальный принтер PDFCreator. И тут снова вступают две особенности:
- Печать происходит в ассинхронном режиме. Т.е. платформа 1С не «ждёт» окончания печати. Данная особенность легко обходится проверкой существования файла.
- Метод Напечатать() у табличного документа не доступен на сервере. И вот это разрешить уже не удаётся — печать происходит из регламентного задания, а они стартуют исключительно в контексте сервера.
//Выполняет сохранение табличного документа ТабДокВход в файл с именем
//ПутьСохраненияВход средствами PDFCreator
Процедура СохранитьPDFЧерезPDFCreator(ТабДокВход, ПутьСохраненияВход)
ПорогСохранения = 3600; //1 час максимально на формирование отчета
ДлинаПаузы = 10;
Если ТипЗнч(ТабДокВход) = Тип("ТабличныйДокумент") Тогда
Попытка
ПутьСохраненияPDF = Константы.Р_ПутьСохраненияPDFCreator.Получить();
ТабДокВход.АвтоМасштаб = Истина;
ТабДокВход.ИмяПринтера = "PDFCreator";
ТабДокВход.Напечатать();
ФайлНаДиске = Новый Файл(ПутьСохраненияPDF);
ДлительностьОжидания = 0;
МожноПродолжать = Истина;
Пока МожноПродолжать Цикл
Если ФайлНаДиске.Существует() Тогда
КопироватьФайл(ПутьСохраненияPDF, ПутьСохраненияВход);
УдалитьФайлы(ПутьСохраненияPDF);
МожноПродолжать = Ложь;
Иначе
Если ДлительностьОжидания < ПорогСохранения Тогда
//Файл ещё не сформирован, ждём
Пауза(ДлинаПаузы);
ДлительностьОжидания = ДлительностьОжидания + ДлинаПаузы;
Иначе
//Время ожидания истекло, сообщаем об этом пользователю
ОбщегоНазначенияУХ.СообщитьОбОшибке("Не найден распечатанный файл PDF Creator : " + Строка(ПутьСохраненияPDF));
МожноПродолжать = Ложь;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Исключение
ОбщегоНазначенияУХ.СообщитьОбОшибке("Во время печати на PDFCreator возникли ошибки: " + ОписаниеОшибки());
КонецПопытки;
Иначе
ОбщегоНазначенияУХ.СообщитьОбОшибке("Неизвестный вариант табличного документа для сохранения в PDF: " + Строка(ТабДокВход));
КонецЕсли;
КонецПроцедуры
OpenOffice.org и другие программы со штатным экспортом в pdf
Следующей на очереди идеей стало сохранение через механизм экспорта бесплатного офисного пакета OpenOffice.org. Схема следующая: сохраняем табличный документ в xls, подключаемся по COM-соединению к OpenOffice.org Calc, а далее пользуемся его механизмом экспорта. Немножко длинная выходит цепочка, но она вполне действенная. И тут нас снова подводит платформа 1С:Предприятия. Сохранение в xls-формат не поддерживает вывод картинки (логотипа) ни в одном из предложенных вариантов (xls97, xls2000, xlsx). Таким образом, сторонние приложения нам не подходят.
//Выполняет сохранение табличного документа ТабДокВход в файл с именем
//ПутьСохраненияВход средствами OpenOffice.org
Процедура СохранитьPDFЧерезOpenOffice(ТабДокВход, ПутьСохраненияВход)
тд = ТабДокВход;
// Открыть OpenOffice
Попытка
ServiceManager = Новый COMОбъект("com.sun.star.ServiceManager");
Исключение
Возврат // опен офис не установлен :-(
КонецПопытки;
ВременныйФайл =ПолучитьИмяВременногоФайла();
ВременныйФайлXLS =ВременныйФайл + ".ods";
ВременныйФайлPDF = ПутьСохраненияВход;
тд.Записать(ВременныйФайлXLS, ТипФайлаТабличногоДокумента.ODS);
// Преобразовываем временный xls файл В PDF
Desktop = ServiceManager.createInstance("com.sun.star.frame.Desktop");
НастройкиОткрытия = Новый COMSafeArray("VT_VARIANT", 1);
PropertyValue = ServiceManager.Bridge_GetStruct("com.sun.star.beans.PropertyValue");
PropertyValue.Name = "Hidden";
PropertyValue.Value = Истина;
НастройкиОткрытия.SetValue(0, PropertyValue);
ВременныйФайлXLS_какУРЛ = "file:///" + СтрЗаменить(ВременныйФайлXLS, "\", "/"); // приводим путь к файлу из виндового формата в опен офисный
// Откроем файл в опене офисе
ОпенОфис = Desktop.LoadComponentFromURL(ВременныйФайлXLS_какУРЛ, "_blank", 0, НастройкиОткрытия);
НастройкиСохранения = Новый COMSafeArray("VT_VARIANT", 1);
PropertyValue = ServiceManager.Bridge_GetStruct("com.sun.star.beans.PropertyValue");
PropertyValue.Name = "FilterName";
PropertyValue.Value = "calc_pdf_Export";
//PropertyValue.Value = "writer_pdf_Export";
НастройкиСохранения.SetValue(0, PropertyValue);
ВременныйФайлPDF_какУРЛ = "file:///" + СтрЗаменить(ВременныйФайлPDF, "\", "/"); // приводим путь к файлу из виндового формата в опен офисный
ОпенОфис.storeToURL(ВременныйФайлPDF_какУРЛ, НастройкиСохранения); // сохранили PDF файл
ОпенОфис.close(-1);
ОпенОфис = Неопределено;
КонецПроцедуры
Yoksel
Во времена далёких 7.7 (а может и позже) была такая библиотека для работы с mxl-форматом от наших соотечественников. Называлась она Yoksel (или в русской транскрипции «Йоксель»). В числе прочего, такая библиотека умела сохранять в pdf табличный документ, переданный ей по COM-соединению. Проблема лишь в том, что библиотека «понимает» лишь формат mxl для платформы 7.7. А в нём снова нет логотипа. Пришлось забраковать и этот вариант.
//Выполняет сохранение табличного документа ТабДокВход в файл с именем
//ПутьСохраненияВход средствами компоненты Yoksel
Процедура СохранитьPDFЧерезYoksel(ТабДокВход, ПутьСохраненияВход)
Попытка
Йоксель = ПолучитьCOMОбъект("","Йоксель");
КонвертерPDF = Йоксель.СоздатьГрафическийКонвертерPDF();
Исключение
ЗаписьЖурналаРегистрации(ОписаниеОшибки(), УровеньЖурналаРегистрации.Ошибка, , ,"Уведомление");
КонецПопытки;
// Конвертируем временный файл в формат pdf
Попытка
Таб = Йоксель.СоздатьТабличныйДокумент();
МояОбласть = ТабДокВход.Область(1, 1, 10, 10);
Таб.ВставитьОбласть(МояОбласть);
КонвертерPDF.Документ = Таб;
КонвертерPDF.КоличествоБитНаПиксел = 24; //1, 4, 8, 24
КонвертерPDF.ЗаписатьВФайл(ПутьСохраненияВход);
Исключение
Сообщить(ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецПроцедуры
В итоге, после рассмотрения всех вариантов, был выбран следующий: используем PDFCreator, но печать вызываем из внешней обработке в режиме обычного клиента, стартующей по расписанию из Windows-планировщика. А как только выходит стабильный релиз 8.3.8 — используем уже типовой механизм сохранения. Помимо этого, 1С пообещала исправить вывод картинок в табличный документ, таким образом, можно будет использовать способ через OpenOffice.org.








[…] Источник […]