Непослушный 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.
Добавить комментарий