pdf logo

Непослушный PDF

На работе встал вопрос периодически (по регламентному заданию) сохранять в pdf-файл отчет из табличного документа. Задача осложнялась тем, что помимо стандартных текста, чисел, таблиц, готовый отчет должен содержать картинку — логотип предприятия.

Механизм платформы

Сначала решить задачу попытались наиболее простым и логичным способом — типовой экспорт табличного документа. Всё бы ничего, но вот в Adobe Reader полученный документ не открывается (ошибка 135). Исследование в hex-редакторе показало, что платформа «ломает» теги шрифта в готовом файле. Эта проблема известна фирме 1С и уже была исправлена (проверено) в релизе 1С:Предприятие 8.3.8, который выйдет предположительно весной сего года. Интересно, что платформа 8.2 такой особенностью не обладает и сохраняет pdf корректно.

ТабличныйДокумент.Записать(ПутьДляСохранения, ТипФайлаТабличногоДокумента.PDF);

PDF Creator и виртуальные принтеры

Невозможность решить проблему типовыми средствами вынудила искать возможности в сторонних средствах. Первое такое решение — воспользоваться виртуальным принтером в pdf. На рынке есть программа PDFCreator, позволяющая выполнять свою главную функцию, «печать» в pdf, бесплатно. Несмотря на кажущуюся сложность, настройка программы элементарна — нужно лишь указать путь сохранения готового файла. После чего, направить исходный файл на виртуальный принтер PDFCreator. И тут снова вступают две особенности:

  1. Печать происходит в ассинхронном режиме. Т.е. платформа 1С не «ждёт» окончания печати. Данная особенность легко обходится проверкой существования файла.
  2. Метод Напечатать() у табличного документа не доступен на сервере. И вот это разрешить уже не удаётся — печать происходит из регламентного задания, а они стартуют исключительно в контексте сервера.
//Выполняет сохранение табличного документа ТабДокВход в файл с именем
//ПутьСохраненияВход средствами 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.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *