Реальный собственный PDF получается экспортом таблицы в память. Для его первой страницы готовятся два ОписаниеПодписиPDF с разными политиками, точным публичным сертификатом и проверенной геометрией. Пакет сохраняет исходные байты и явный запрет подписания: закрытого ключа нет.
Где и как запускать
Полный серверный общий модуль своей пустой учебной конфигурации: реальный PDF в памяти и проверка разметки, без фактического подписания. В1С не запускался.
Подготовка
НОВАЯ отдельная пустая конфигурация УчебнаяРазметкаПодписиPDF85 на8.5.1.1343, совместимость Не использовать, Русский. Без рабочих каталогов, документов, регистров, общих реквизитов, расширений, подписок и заданий. Только свои модуль, форма и команда этого опыта.
Конфигуратор → Общие модули → УчебнаяРазметкаПодписиPDF85: Сервер=Да, Вызов сервера=Да, Клиент=Нет, Глобальный=Нет, Привилегированный=Нет. Вставьте весь основной модуль, включая защиту пустого стенда и обе полные функции экспорта/чтения PDF.
Своя обработка или общая управляемая форма УчебнаяРазметкаПодписиPDF85: команда/кнопка Выполнить, действие Выполнить. Вставьте весь supporting-модуль ниже. Будущая ручная подготовка: сохранить конфигурацию, обновить только свою базу F7, открыть эту форму и нажать её собственную кнопку.
Все PDF-байты, таблица Книга/3/250/750, описания и штамп создаются в памяти. Нет исходного файла с неизвестным происхождением, чужого печатного макета, сертификата пользователя ОС или произвольного ИсходныйОбъект.
Публичный DER Syntax Guide Training Only встроен полностью и сверяется по точному отпечатку. Он не доверенный, не установлен в ОС, его закрытого ключа в уроке нет. Не заменяйте его рабочим PFX и не добавляйте вызов подписания.
Это серверная подготовка разметки на собственном Windows-стенде, не запуск криптопровайдера. Ни BSL, ни экспорт PDF в1С, ни GUI, ни подпись, ни хранилище ОС здесь не запускались.
Процедура ПроверитьСвойПустойСтенд()
Если Метаданные.Справочники.Количество() <> 0 Или Метаданные.Документы.Количество() <> 0 Или Метаданные.Перечисления.Количество() <> 0 Или Метаданные.РегистрыСведений.Количество() <> 0 Или Метаданные.РегистрыНакопления.Количество() <> 0 Или Метаданные.РегистрыБухгалтерии.Количество() <> 0 Или Метаданные.РегистрыРасчета.Количество() <> 0 Или Метаданные.ПланыВидовХарактеристик.Количество() <> 0 Или Метаданные.ПланыВидовРасчета.Количество() <> 0 Или Метаданные.ПланыСчетов.Количество() <> 0 Или Метаданные.ПланыОбмена.Количество() <> 0 Или Метаданные.Последовательности.Количество() <> 0 Или Метаданные.БизнесПроцессы.Количество() <> 0 Или Метаданные.Задачи.Количество() <> 0 Или Метаданные.ОбщиеРеквизиты.Количество() <> 0 Или Метаданные.ПодпискиНаСобытия.Количество() <> 0 Или Метаданные.РегламентныеЗадания.Количество() <> 0 Или Метаданные.ВнешниеИсточникиДанных.Количество() <> 0 Тогда
ВызватьИсключение "Нужен только свой новый пустой учебный стенд";
КонецЕсли;
Если Метаданные.ЕстьИзмененияРасширениямиКонфигурации() Тогда
ВызватьИсключение "Свой сеанс не должен изменяться расширениями";
КонецЕсли;
Источники = Новый Массив;
Источники.Добавить(ИсточникРасширенийКонфигурации.БазаДанных);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансАктивные);
Источники.Добавить(ИсточникРасширенийКонфигурации.СеансОтключенные);
Для Каждого Источник Из Источники Цикл
Расширения = РасширенияКонфигурации.Получить(, Источник);
Если ТипЗнч(Расширения) <> Тип("Массив") Тогда
ВызватьИсключение "Native список расширений должен иметь тип Массив";
КонецЕсли;
Если Расширения.Количество() <> 0 Тогда
ВызватьИсключение "В своём новом стенде нет расширений базы или сеанса";
КонецЕсли;
КонецЦикла;
Если ТранзакцияАктивна() Тогда
ВызватьИсключение "Чужая или вложенная активная транзакция не допускается";
КонецЕсли;
КонецПроцедуры
Функция СоздатьСобственныеБайты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;
Документ.Прочитать(Поток);
Поток.Закрыть();
Исключение
Поток.Закрыть();
ВызватьИсключение;
КонецПопытки;
Возврат Документ;
КонецФункции
Функция СоздатьСвойШтамп()
Штамп = Новый ТабличныйДокумент;
Штамп.Область(1, 1).Текст = "УЧЕБНАЯ РАЗМЕТКА — НЕ ПОДПИСАНО";
Штамп.Область(2, 1).Текст = "Книга / 3 / 250 / 750";
Возврат Штамп;
КонецФункции
Функция СоздатьСвоёОписание(Сертификат, ВидПодписи, Имя)
Описание = Новый ОписаниеПодписиPDF;
Описание.Сертификат = Сертификат;
Описание.ТипПодписи = ВидПодписи;
Описание.Имя = Имя;
Описание.НомерСтраницы = 1;
Описание.Лево = 10;
Описание.Верх = 45;
Описание.Ширина = 70;
Описание.Высота = 15;
Если ВидПодписи = ТипПодписиPDF.Утверждающая Тогда
Описание.БлокировкаДокумента = Истина;
Описание.РазрешенияДоступаИзменения = РазрешенияДоступаИзмененияPDF.Нет;
ИначеЕсли ВидПодписи = ТипПодписиPDF.Сертифицирующая Тогда
Описание.БлокировкаДокумента = Ложь;
Описание.РазрешенияДоступаИзменения = РазрешенияДоступаИзмененияPDF.ЗаполнениеПодписание;
Иначе
ВызватьИсключение "Урок допускает только две явно разобранные политики PDF";
КонецЕсли;
Возврат Описание;
КонецФункции
Процедура ПроверитьСвоюРазметку(Документ, Описание, Сертификат, Имя, ВидПодписи)
Если ТипЗнч(Описание) <> Тип("ОписаниеПодписиPDF") Или Описание.Имя <> Имя
Или Описание.ТипПодписи <> ВидПодписи Тогда
ВызватьИсключение "Получены не собственные имя и политика подписи";
КонецЕсли;
Если ТипЗнч(Описание.Сертификат) <> Тип("СертификатКриптографии")
Или Base64Строка(Описание.Сертификат.Отпечаток) <> Base64Строка(Сертификат.Отпечаток) Тогда
ВызватьИсключение "Описание должно содержать только собственный публичный образец";
КонецЕсли;
Если Описание.НомерСтраницы <> 1 Или Описание.НомерСтраницы > Документ.КоличествоСтраниц() Тогда
ВызватьИсключение "Нужна существующая первая страница своего документа";
КонецЕсли;
Страница = Документ.Страницы.Получить(Описание.НомерСтраницы - 1);
Если Страница.Ориентация <> 0 Тогда
ВызватьИсключение "Простая проверка урока рассчитана только на неповёрнутую собственную страницу";
КонецЕсли;
ШиринаРабочейОбласти = Страница.Ширина - Страница.ПолеСлева - Страница.ПолеСправа;
ВысотаРабочейОбласти = Страница.Высота - Страница.ПолеСверху - Страница.ПолеСнизу;
Если Описание.Лево < 0 Или Описание.Верх < 0 Или Описание.Ширина <= 0 Или Описание.Высота <= 0
Или Описание.Лево + Описание.Ширина > ШиринаРабочейОбласти
Или Описание.Верх + Описание.Высота > ВысотаРабочейОбласти Тогда
ВызватьИсключение "Учебный прямоугольник не помещается в консервативную область страницы без её полей";
КонецЕсли;
Если ТипЗнч(Описание.БлокировкаДокумента) <> Тип("Булево") Тогда
ВызватьИсключение "Признак блокировки должен быть Булево";
КонецЕсли;
Если ВидПодписи = ТипПодписиPDF.Утверждающая Тогда
Если Не Описание.БлокировкаДокумента
Или Описание.РазрешенияДоступаИзменения <> РазрешенияДоступаИзмененияPDF.Нет Тогда
ВызватьИсключение "Своя утверждающая политика должна задавать блокировку, не разрешение заполнения";
КонецЕсли;
Иначе
Если Описание.БлокировкаДокумента
Или Описание.РазрешенияДоступаИзменения <> РазрешенияДоступаИзмененияPDF.ЗаполнениеПодписание Тогда
ВызватьИсключение "Своя сертифицирующая политика должна разрешать заполнение/подписание, не блокировку";
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Процедура ВыполнитьПример() Экспорт
ПроверитьСвойПустойСтенд();
ДанныеPDF = СоздатьСобственныеБайтыPDF();
Документ = ПрочитатьСобственныйДокумент(ДанныеPDF);
Если ДанныеPDF.Размер() = 0 Или Документ.КоличествоСтраниц() = 0 Тогда
ВызватьИсключение "Нужен реальный собственный PDF, а не пустая переменная";
КонецЕсли;
Сертификат = Новый СертификатКриптографии(Base64Значение("MIIC+jCCAeKgAwIBAgIBVTANBgkqhkiG9w0BAQsFADAlMSMwIQYDVQQDDBpTeW50YXggR3VpZGUgVHJhaW5pbmcgT25seTAeFw0yMDAxMDEwMDAwMDBaFw0zNTEyMzEwMDAwMDBaMCUxIzAhBgNVBAMMGlN5bnRheCBHdWlkZSBUcmFpbmluZyBPbmx5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkraGtF6mSMHQWI0lIdDxzNjLTB0VHmtLUYEIxXFFJepQtyMGWZ9rfpbTglFp8wpsw+Wc09FQyDFuKsNz3cBcBBx7fTmJVuYzfrXjhDECNqEoKMEf+Qrim3WsVKA/Qr8IMnzq1qdnA1pLgaaKyscfCJkw9W1yoTlLV3PmGs6h6gP1/KXwe+zKN5r3Jl0IVk8RXTLAeyqN01lCuyzaeSYm/v2YS88LdaJ/nkdFqy5QjRXfm7EOCW7eCZlCBwW0EvGhUarZxWlq04lPs9qu3aC3/6gY/scE2vY1jeZV2VpV0lLQpU5gVdjVGI4YQmfgkb8jYtV5U7FQ1KQ6Poji/ort6wIDAQABozUwMzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEARoo1FJJKKkaZXyFl1tlYj8K7xvryXwhb+tCaFvdXuON27YC3/YIHLUT9sCF9o76VIT8dsVMpK0Qo+VV0FIvqRcB+u6SmyJ+Y7590mzd5P1CSt8Y/4BNV2LosYxhcdVczs26PWHL5GXyBMWRxtBjNsnTeMKh4/WIMrplAr0IOJCmjIeKhv0JbQacsNqhmpe7JdgtBYC4Rrn3ajdD99HZREmyLR/iJrh6W4IGqSN0MGT0xL/mLI0GsTp9JhHDF3o8bmC1yMDlLOoAZp7M072bzfDsKFPg4ATQp/hhbtsTdS/LXrd9cLehZ7ThH6ljL8y2JbALD+6HPnT6Sv+Mg0Wq0MA=="));
Если Base64Строка(Сертификат.Отпечаток) <> "tRDNiwoaiFssHhQqwyjWVlPLdns=" Тогда
ВызватьИсключение "Публичный DER-образец не совпал с зафиксированным отпечатком";
КонецЕсли;
Утверждающая = СоздатьСвоёОписание(Сертификат, ТипПодписиPDF.Утверждающая, "СвояРазметкаУтверждения85");
Сертифицирующая = СоздатьСвоёОписание(Сертификат, ТипПодписиPDF.Сертифицирующая, "СвояРазметкаСертификации85");
ПроверитьСвоюРазметку(Документ, Утверждающая, Сертификат, "СвояРазметкаУтверждения85", ТипПодписиPDF.Утверждающая);
ПроверитьСвоюРазметку(Документ, Сертифицирующая, Сертификат, "СвояРазметкаСертификации85", ТипПодписиPDF.Сертифицирующая);
Штамп = СоздатьСвойШтамп();
Пакет = Новый Структура("PDF,Представление,Утверждающая,Сертифицирующая,МожноПодписывать", ДанныеPDF, Штамп, Утверждающая, Сертифицирующая, Ложь);
Если Пакет.МожноПодписывать Или Пакет.PDF.Размер() <> ДанныеPDF.Размер()
Или Пакет.Представление.Область(1, 1).Текст <> "УЧЕБНАЯ РАЗМЕТКА — НЕ ПОДПИСАНО"
Или Пакет.Утверждающая.Имя = Пакет.Сертифицирующая.Имя Тогда
ВызватьИсключение "Пакет должен сохранить свой исходный PDF, две разные альтернативы и запрет подписания";
КонецЕсли;
Сообщить("Подготовлены две АЛЬТЕРНАТИВНЫЕ разметки, не две подписи в документе");
Сообщить("Свой прямоугольник, мм: " + Строка(Утверждающая.Лево) + "/" + Строка(Утверждающая.Верх)
+ "/" + Строка(Утверждающая.Ширина) + "/" + Строка(Утверждающая.Высота));
Сообщить("МожноПодписывать=Ложь. Исходный PDF не подписан и не заблокирован");
КонецПроцедурыУчебнаяРазметкаПодписиPDF85
Своя управляемая форма → Модуль
Полный клиентский launcher с server wrapper для собственного общего модуля; не выполнялся.
&НаКлиенте
Процедура Выполнить(Команда)
ВыполнитьНаСервере();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ВыполнитьНаСервере()
УчебнаяРазметкаПодписиPDF85.ВыполнитьПример();
КонецПроцедурыРазбор по шагам
Полные функции создают свою таблицу, записывают её в ПотокВПамяти как PDF, закрывают поток с получением ДвоичныеДанные и читают настоящий ДокументPDF отдельным потоком. Оба потока закрываются также при ошибке.
ДО получения страницы проверяется наличие фактических PDF-байтов и страниц. НомерСтраницы у описания начинается с1; Получить коллекции — с0. Поэтому индекс вычисляется НомерСтраницы-1, а не подставляется тот же номер.
СоздатьСвоёОписание действительно конструирует ОписаниеПодписиPDF. В него записываются свой сертификат, точные разные имена, существующая первая страница и прямоугольник10/45/70/15мм: Лево/Верх — позиция, Ширина/Высота — размеры.
Отпечаток встроенного DER проверяется до подготовки описаний. Проверка разметки затем повторно сверяет реальный тип и отпечаток Описание.Сертификат: похожее имя CN не заменяет принадлежность учебному образцу.
Не предполагаем форматA4: читаем реальные Ширина/Высота страницы и четыре поля. Консервативная область вычисляется без полей, затем проверяется правая и нижняя граница прямоугольника. Для поворота страницы не0 алгоритм честно отказывает, а не меняет местами ширину и высоту наугад.
В утверждающей альтернативе БлокировкаДокумента=Истина. РазрешенияДоступаИзменения задаются Нет как явное нейтральное значение; это свойство имеет смысл для сертифицирующего типа, не управляет утверждающей подписью.
В сертифицирующей альтернативе БлокировкаДокумента=Ложь и РазрешенияДоступаИзменения=ЗаполнениеПодписание. Это разрешение заполнения форм и добавления подписей, не произвольного изменения текста/страниц. Блокировка имеет смысл для утверждающего типа.
Проверяются тип описания, имя, тип подписи, собственный сертификат, реальная страница, положительные размеры, границы и согласованность каждой политики. Это прикладной preflight: платформа не обязана проверять нашу геометрию при самом присваивании свойств.
Создаётся собственный ТабличныйДокумент-штамп с явным текстом НЕ ПОДПИСАНО. Пакет содержит исходные PDF-байты, представление и ДВЕ АЛЬТЕРНАТИВЫ описания. Они не добавлены в PDF; результат дополнительно требует МожноПодписывать=Ложь.
Подписание требует отдельного настоящего менеджера криптографии и собственного закрытого ключа. Наличие публичного СертификатКриптографии не обеспечивает ни того, ни другого. Этот пакет предназначен для разработки/проверки разметки и не передаётся ДобавитьПодпись.
Readonly Дата намеренно не назначается и не считается покрытой этим уроком: дату настоящей подписи нужно читать из описания реально подписанного PDF. Дата нового шаблона не доказательство события подписания.
Как проверить результат
Будущий результат: две разметки проходят геометрию и разные проверки политики, имена различны; вывод прямо сообщает МожноПодписывать=Ложь. Исходный PDF остаётся неподписанным; штамп лежит отдельно в памяти.
Будущий отрицательный контроль: в СоздатьСвоёОписание задайте Ширина=0. ПроверитьСвоюРазметку должна выбросить исключение ДО формирования итогового пакета, а не вывести сообщение об успехе.
Будущий контроль страницы: в начале ПроверитьСвоюРазметку временно вставьте Описание.НомерСтраницы = Документ.КоличествоСтраниц() + 1; оба объекта — реальные параметры этой процедуры. Проверка должна остановиться до Получить. Уберите эту строку после опыта.
Будущий контроль политики: у сертифицирующей альтернативы временно установите БлокировкаДокумента=Истина или разрешение Нет. Полная проверка ожидает заданную для урока комбинацию и должна отказать.
runtimeVerifiedScenarios=0. Все указанные результаты — контроль будущего ручного запуска на своём стенде, не выполненный экспорт, не визуальная проверка штампа и не доказательство криптографической действительности.
Ожидаемый результат
Реальный собственный PDF и две проверяемые альтернативы разметки собраны в памяти; подписывание явно запрещено, исходный документ не изменён
На что обратить внимание
Присваивание настроек подписи само по себе не подписывает, не сертифицирует и не блокирует PDF. Тип/разрешения показывают будущую политику, а не уже действующее ограничение исходного документа.
Два описания — альтернативы одной задачи, не инструкция ставить две несовместимые сертифицирующие подписи подряд. Какую политику применять к реальному документу, решают отдельно до подписания.
Координаты подписи — миллиметры, не пиксели и не номера строк табличного документа. Границы проверены консервативно по реальной неповёрнутой странице; внешний вид и поворот штампа требуют отдельного будущего визуального/платформенного опыта.
Открытый сертификат не содержит закрытого ключа. Проверка отпечатка удостоверяет только конкретные байты учебного образца, не доверенную цепочку, юридическую силу, криптографическую пригодность или доступ к контейнеру.
Не присваивайте readonly Дата, не рисуйте вместо неё текущую дату и не называйте вывод НЕ ПОДПИСАНО электронной подписью. Для Дата нужен другой полный урок с собственным ключом и чтением фактических описаний PDF.
Файлы, ОС-хранилища, криптопровайдеры, PIN, OCSP/CRL, сеть, печать и рабочие данные этим примером не используются. ПотокВПамяти не является файлом на диске.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.