Записать свой PDF пятью callback-шагами и прочитать результат после закрытия

Учебные задачи

Открытие → вложение → штамп → удаление собственного вложения → закрытие. Все завершения писателя принимают только Состояние, не выдуманный Результат.

Где и как запускать

Полный модуль управляемой формы внешней обработки, толстый клиент Windows; свой файл на клиенте.

Подготовка

  1. Работайте только в отдельной своей конфигурации УчебнаяКонфигурацияСинтаксиса, 8.5.1.1343. Исходная таблица Книга / 3 / 250 / 750 создаётся полностью в коде.

  2. Для синхронных уроков вставьте полный модуль в серверный общий модуль УчебныеФайлыPDF и вызовите ВыполнитьПример() своей серверной командой. Новый временный файл будет на серверной машине, не обязательно на вашем ПК.

  3. Для асинхронного и callback-урока вставьте полный код в модуль управляемой формы внешней обработки. Создайте команду Выполнить, обработчик Выполнить, кнопку. Используйте толстый клиент Windows; файл создаётся на клиентской машине.

  4. Код сначала получает новое временное имя, проверяет отсутствие файла и выводит точный путь. Только после этого записывает собственные PDF-байты. Рабочий или выбранный пользователем PDF нигде не запрашивается.

  5. ЗаписьPDF изменяет именно созданный этим запуском файл. Чтение результата начинается после закрытия записи. Файл оставляется пользователю для просмотра и ручного удаления; автоматического удаления нет.

  6. Удаление вложений допустимо начиная с PDF 1.5. Пример проверяет первые восемь символов собственного файла перед этой операцией. Для PDF/A-1 или PDF/A-2 произвольные CSV-вложения не подходят.

  7. До завершения асинхронной цепочки форму не закрывайте и второй запуск не выполняйте. При ошибке начала шага успех не объявляется. Ни создание пособия, ни его Python-тесты этот код в 1С не выполняют.

Перем Занято;
Перем СобственноеСостояние;
Перем СобственныйPDF;

&НаСервереБезКонтекста
Функция СоздатьСобственныеБайтыPDF()
    Таблица = Новый ТабличныйДокумент;
    Таблица.КодЯзыка = "ru";
    Таблица.Область(1, 1).Текст = "СОБСТВЕННЫЙ УЧЕБНЫЙ ОБРАЗЕЦ";
    Таблица.Область(2, 1).Текст = "Книга";
    Таблица.Область(2, 2).Текст = "3";
    Таблица.Область(2, 3).Текст = "250";
    Таблица.Область(2, 4).Текст = "750";
    Таблица.Область(1, 1, 2, 4).ЦветФона = Новый Цвет(255, 255, 210);
    Таблица.Область(, 1, , 1).ШиринаКолонки = 45;
    Поток = Новый ПотокВПамяти;
    Попытка
        Таблица.Записать(Поток, ТипФайлаТабличногоДокумента.PDF);
        Данные = Поток.ЗакрытьИПолучитьДвоичныеДанные();
    Исключение
        Поток.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Возврат Данные;
КонецФункции

&НаКлиенте
Функция СоздатьСвойФайлPDF(Данные)
    Имя = ПолучитьИмяВременногоФайла("pdf");
    Проверка = Новый Файл(Имя);
    Если Проверка.Существует() Тогда
        ВызватьИсключение "Новое имя уже занято: перезапись запрещена";
    КонецЕсли;
    Сообщить("Создаётся только собственный PDF: " + Имя);
    Данные.Записать(Имя);
    Возврат Имя;
КонецФункции

&НаКлиенте
Функция ПрочитатьСвоиВложения(Имя)
    Чтение = Новый ЧтениеPDF(Имя);
    Попытка
        Вложения = Чтение.ПолучитьВложения();
        Чтение.Закрыть();
    Исключение
        Чтение.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Возврат Вложения;
КонецФункции

&НаКлиенте
Процедура ПроверитьУчебнуюВерсию(Имя)
    Чтение = Новый ЧтениеДанных(Имя, КодировкаТекста.UTF8);
    Попытка
        Заголовок = Чтение.ПрочитатьСимволы(8);
        Чтение.Закрыть();
    Исключение
        Чтение.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Если СтрДлина(Заголовок) <> 8 Или Лев(Заголовок, 5) <> "%PDF-" Или Сред(Заголовок, 7, 1) <> "." Тогда
        ВызватьИсключение "Учебный заголовок PDF не распознан";
    КонецЕсли;
    Старшая = Число(Сред(Заголовок, 6, 1));
    Младшая = Число(Сред(Заголовок, 8, 1));
    Если Старшая < 1 Или (Старшая = 1 И Младшая < 5) Тогда
        ВызватьИсключение "Удаление вложений требует PDF 1.5 или новее";
    КонецЕсли;
    Сообщить("Заголовок собственного файла: " + Заголовок);
КонецПроцедуры

&НаКлиенте
Функция СоздатьСвоёВложение(Текст)
    Вложения = Новый КоллекцияВложенийPDF;
    Данные = ПолучитьДвоичныеДанныеИзСтроки(Текст, КодировкаТекста.UTF8, Ложь);
    Вложения.Добавить("guide-owned-book.csv", Данные, "text/csv", ТипСвязиВложенияPDF.Данные);
    Возврат Вложения;
КонецФункции

&НаКлиенте
Процедура ПроверитьСвоюСтраницу(Данные)
    Поток = Данные.ОткрытьПотокДляЧтения();
    Попытка
        Документ = Новый ДокументPDF;
        Документ.Прочитать(Поток);
        Поток.Закрыть();
    Исключение
        Поток.Закрыть();
        ВызватьИсключение;
    КонецПопытки;
    Если Документ.КоличествоСтраниц() = 0 Тогда
        ВызватьИсключение "Нет собственной страницы для штампа";
    КонецЕсли;
    Первая = Документ.Страницы.Получить(0);
    Если Первая.Ширина < 80 Или Первая.Высота < 65 Тогда
        ВызватьИсключение "Собственная страница меньше прямоугольника штампа";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Функция СоздатьСобственныйШтамп()
    ТаблицаШтампа = Новый ТабличныйДокумент;
    ТаблицаШтампа.КодЯзыка = "ru";
    ТаблицаШтампа.Область(1, 1).Текст = "ОБРАЗЕЦ — НЕ ДОКУМЕНТ";
    ТаблицаШтампа.Область(1, 1).ЦветФона = Новый Цвет(210, 240, 255);
    Описание = Новый ОписаниеОтображаемогоОбъектаPDF;
    Описание.Имя = "guide-owned-stamp";
    Описание.Объект = ТаблицаШтампа;
    Описание.НомерСтраницы = 1;
    Описание.Лево = 10;
    Описание.Верх = 45;
    Описание.Ширина = 60;
    Описание.Высота = 10;
    Возврат Описание;
КонецФункции

&НаКлиенте
Функция ОповещениеЗаписиPDF(Имя, Состояние)
    Возврат Новый ОписаниеОповещения(Имя, ЭтотОбъект, Состояние, "ОшибкаЗаписиPDF", ЭтотОбъект);
КонецФункции

&НаКлиенте
Процедура ПроверитьСостояниеЗаписи(Состояние)
    Если Состояние <> СобственноеСостояние Или Состояние.Маркер <> "guide-owned-writer" Тогда
        ВызватьИсключение "Неизвестное состояние PDF-писателя";
    КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура НачатьСледующийШаг(Состояние, Шаг)
    ПроверитьСостояниеЗаписи(Состояние);
    Попытка
        Если Шаг = 1 Тогда
            Состояние.Запись.НачатьЗаписьВложений(ОповещениеЗаписиPDF("ПослеВложения", Состояние), Состояние.Вложения);
        ИначеЕсли Шаг = 2 Тогда
            Состояние.Запись.НачатьЗаписьОтображаемогоОбъекта(ОповещениеЗаписиPDF("ПослеШтампа", Состояние), Состояние.Описание);
        ИначеЕсли Шаг = 3 Тогда
            Состояние.Запись.НачатьЗаписьУдаленияВложений(ОповещениеЗаписиPDF("ПослеУдаления", Состояние), Состояние.Имена);
        ИначеЕсли Шаг = 4 Тогда
            Состояние.Запись.НачатьЗакрытие(ОповещениеЗаписиPDF("ПослеЗакрытияЗаписи", Состояние));
        Иначе
            ВызватьИсключение "Неизвестный номер учебного шага";
        КонецЕсли;
    Исключение
        Сообщить("Начало шага не подтверждено: " + ОписаниеОшибки());
        Сообщить("Повтор запрещён; закройте только собственную учебную форму. Свой путь: " + Состояние.Имя);
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура Выполнить(Команда)
    Если Занято = Истина Тогда
        ВызватьИсключение "Собственная цепочка записи уже запущена";
    КонецЕсли;
    Данные = СоздатьСобственныеБайтыPDF();
    ПроверитьСвоюСтраницу(Данные);
    Имя = СоздатьСвойФайлPDF(Данные);
    ПроверитьУчебнуюВерсию(Имя);
    Имена = Новый Массив;
    Имена.Добавить("guide-owned-book.csv");
    Состояние = Новый Структура("Маркер,Имя,Запись,Вложения,Описание,Имена", "guide-owned-writer", Имя, Новый ЗаписьPDF,
        СоздатьСвоёВложение("Книга;3;250;750"), СоздатьСобственныйШтамп(), Имена);
    СобственноеСостояние = Состояние;
    Занято = Истина;
    Попытка
        Состояние.Запись.НачатьОткрытие(ОповещениеЗаписиPDF("ПослеОткрытияЗаписи", Состояние), Имя);
    Исключение
        Сообщить("Начало записи не подтверждено: " + ОписаниеОшибки());
        Сообщить("Повтор запрещён; закройте только собственную учебную форму. Свой путь: " + Имя);
    КонецПопытки;
КонецПроцедуры

&НаКлиенте
Процедура ПослеОткрытияЗаписи(Состояние) Экспорт
    НачатьСледующийШаг(Состояние, 1);
КонецПроцедуры

&НаКлиенте
Процедура ПослеВложения(Состояние) Экспорт
    НачатьСледующийШаг(Состояние, 2);
КонецПроцедуры

&НаКлиенте
Процедура ПослеШтампа(Состояние) Экспорт
    НачатьСледующийШаг(Состояние, 3);
КонецПроцедуры

&НаКлиенте
Процедура ПослеУдаления(Состояние) Экспорт
    НачатьСледующийШаг(Состояние, 4);
КонецПроцедуры

&НаКлиенте
Процедура ПослеЗакрытияЗаписи(Состояние) Экспорт
    ПроверитьСостояниеЗаписи(Состояние);
    Итог = ПрочитатьСвоиВложения(Состояние.Имя);
    Сообщить(Итог.Количество() = 0);
    СобственныйPDF = Новый ДокументPDF;
    СобственныйPDF.Прочитать(Состояние.Имя);
    СобственныйPDF.Показать("guide-owned: запись PDF завершена callback", "");
    СобственноеСостояние = Неопределено;
    Занято = Ложь;
КонецПроцедуры

&НаКлиенте
Процедура ОшибкаЗаписиPDF(ИнформацияОбОшибке, Состояние, СтандартнаяОбработка) Экспорт
    ПроверитьСостояниеЗаписи(Состояние);
    СтандартнаяОбработка = Ложь;
    Сообщить("Ошибка своей записи PDF: " + ИнформацияОбОшибке.Описание);
    Попытка
        Состояние.Запись.Закрыть();
    Исключение
        Сообщить("Закрытие не подтверждено: " + ОписаниеОшибки());
        Сообщить("Повтор запрещён; закройте только собственную учебную форму. Свой путь: " + Состояние.Имя);
        Возврат;
    КонецПопытки;
    СобственноеСостояние = Неопределено;
    Занято = Ложь;
КонецПроцедуры

Разбор по шагам

  1. Создать только свой PDF-файл и подготовить собственное вложение, штамп и точное имя удаления.

  2. Сохранить состояние в переменной формы. После завершения каждого шага запускать следующий, не все Начать подряд.

  3. Все пять экспортных callback писателя имеют один аргумент Состояние. Три аргумента отдельно используются обработчиком ошибки.

  4. Читать итоговые вложения и открывать документ только в callback подтверждённого закрытия писателя.

  5. Если начало шага или закрытие не подтверждены, не снимать запрет повторного запуска и не считать файл готовым. Пользователь закрывает только учебную форму.

Как проверить результат

  1. После подтверждённого закрытия итоговая коллекция вложений пуста: проверка Истина.

  2. В своём окне видны книга и штамп; путь выведен до создания и принадлежит только этому запуску. Визуальный результат проверяется вручную в 1С.

Ожидаемый результат

Полная цепочка callback PDF-писателя с правильной границей жизненного цикла и владения файлом.

На что обратить внимание

  1. У callback записи нет Булево результата: подтверждение приходит фактом завершения либо отдельным обработчиком ошибки.

  2. НачатьЗаписьУдаленияВложений принимает массив имён, не коллекцию объектов вложений.

  3. Файл остаётся для ручного просмотра и удаления. Не выполняйте очистку всего временного каталога или всех вложений чужого PDF.

Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.

Не хотите разбираться сами?

Адаптируем это решение под вашу базу. Оценка бесплатно.

Оставить задачу

1С, 1С:Предприятие — товарные знаки ООО «1С». Сайт не является официальным представительством фирмы «1С» и не аффилирован с ней.