Origami PDF

Origami PDF позволяет разбирать структуру PDF до отдельных объектов, извлекать вложения, изображения, шрифты и JavaScript, снимать и устанавливать шифрование, проверять документ по политикам безопасности и собирать новые файлы через Ruby-сценарии. Набор команд подходит для анализа, пакетной обработки и воспроизводимых операций, где важно видеть не только страницы, но и каталоги, потоки, ссылки, формы, метаданные и действия внутри документа.

Основной рабочий процесс строится вокруг отдельных команд: сначала файл проверяют через pdfcop или читают его метаданные, затем при необходимости распаковывают потоки, извлекают ресурсы и создают изменённую копию. Для задач, которые не укладываются в готовую команду, документ открывают методом PDF.read, обходят страницы и объекты в Ruby-коде, меняют нужные словари или потоки и сохраняют результат под новым именем.

Визуального редактора страниц здесь нет: управление выполняется параметрами командной строки, а подробное исследование объекта продолжается в интерактивной оболочке или Ruby-сценарии. Такой подход требует аккуратной работы с копиями файлов, зато позволяет повторить процедуру на сотнях документов, записать каждый шаг в сценарий и проверить, какие именно структуры были изменены.

Скачать Origami PDF

Оценка 9.7 Рекомендуем
  • Редактирование PDF
  • Русский интерфейс
  • Просто новичкам
Скачать бесплатно на Windows
Лучшая альтернатива
Origami PDF
Оценка 8.5
  • Нет визуального редактора
  • Нужны Ruby-команды
  • Нет русского интерфейса
Скачать Origami PDF
Загрузка начнётся после нажатия

Как устроен рабочий процесс Origami PDF

Команды работают с внутренним представлением документа: таблицами перекрёстных ссылок, косвенными объектами, словарями, массивами, строками, именами и потоками. Поэтому перед запуском формулируют структурную цель. Для проверки подозрительного файла это поиск OpenAction и JavaScript, для миграции — извлечение метаданных, для отладки генератора — сравнение объектных деревьев до и после преобразования. Обычное визуальное совпадение страниц не показывает таких различий.

Безопасная последовательность начинается с неизменяемой копии. Исходному файлу присваивают контрольную сумму, результаты пишут в отдельный каталог, а команды запускают без административных прав. Если документ получен извне, его не открывают в привычном просмотрщике до инвентаризации активного содержимого. Особенно осторожно обращаются с вложениями, сценариями, Launch, URI и действиями форм, потому что они не обязаны быть заметны на странице.

Каждая утилита решает узкую задачу и принимает имя PDF вместе с параметрами. Вывод можно направить в файл, каталог или стандартный поток в зависимости от команды. Это удобно для конвейеров: JSON из pdfmetadata передают обработчику, декомпрессированную копию сравнивают средствами контроля версий, а каталог pdfextract проверяют отдельными анализаторами изображений, шрифтов и сценариев. Код завершения сохраняют вместе с журналом.

Параметры проверки PDF командой pdfcop

Установка и проверка запуска

Для установки через RubyGems используют менеджер gem, после чего проверяют наличие pdfcop, pdfextract, pdfmetadata и pdfsh в каталоге исполняемых файлов Ruby. В Debian командами управляет пакет origami-pdf, а библиотеку предоставляет зависимость ruby-origami. Ручное копирование одного файла не работает: исполняемые сценарии загружают классы парсера, фильтров, шифрования, форм и объектов PDF из Ruby-библиотеки.

Разработчик указывает минимальное требование Ruby 2.1. В новых средах отдельные части стандартной библиотеки могут устанавливаться как самостоятельные gem-пакеты. Если запуск завершается LoadError с упоминанием matrix или rexml, устанавливают отсутствующий модуль именно в тот gemset, из которого запускается команда. Затем повторяют справку, а не сразу обрабатывают важный документ. Смешение системного Ruby и пользовательского менеджера версий часто объясняет несовпадение путей.

После установки выполняют три теста. Сначала открывают справку каждой команды, затем читают небольшой известный PDF и, наконец, создают минимальный документ через PDF.new или PDF.write. Первый тест подтверждает PATH, второй — работу парсера и декодеров, третий — сериализацию. Если сбой появляется только на одном файле, фиксируют номер объекта и фильтр: причина вероятнее связана со структурой входа, а не с общей установкой.

Чтение PDF и режим lazy

Метод PDF.read возвращает объект документа с доступом к страницам и внутренним элементам. По умолчанию содержимое разбирается сразу. Параметр lazy: true откладывает разбор до обращения к объекту. На крупном файле это сокращает стартовую работу, если требуется исследовать только каталог, метаданные, набор страниц или конкретные ресурсы. Режим следует указывать явно в сценарии, чтобы повторный запуск использовал ту же стратегию.

Ленивое чтение не исправляет повреждённую таблицу ссылок. Трейлер, xref и объекты, нужные для навигации, должны быть достаточно согласованы. Если ошибка возникает при первом обращении к потоку, записывают reference, Filter, DecodeParms и заявленную Length. Затем объект извлекают отдельно и сравнивают поведение с другим структурным инструментом. Молчаливое переключение на полное чтение может изменить место ошибки, но не устранить её.

Обход страниц выполняют через pages или each_page, а общий граф — через итераторы объектов. Ссылка не равна содержимому: её сначала разрешают, после чего проверяют фактический класс. Перед изменением словаря подтверждают наличие ключа и допустимый тип значения. Такой код устойчивее к файлам, где одинаковая семантика представлена прямым объектом, косвенной ссылкой или наследуемым значением.

Метаданные через pdfmetadata

pdfmetadata показывает словарь Document Information и XMP. Это независимые слои: Author, Title, Creator, Producer и даты могут не совпадать. При расследовании расхождение не исправляют автоматически, потому что оно может отражать несколько этапов обработки. Сначала сохраняют исходный вывод и контрольную сумму, затем нормализуют даты и кодировки в отдельной аналитической таблице. Поля метаданных свободно редактируются и сами по себе не доказывают происхождение.

Формат JSON подходит для пакетной проверки. В него добавляют имя файла, размер и контрольную сумму, после чего ищут необычные Producer, дату изменения раньше создания, отсутствующий XMP или повторяющиеся наборы значений. Текстовый формат удобнее для разового чтения. Цвет терминала отключают при перенаправлении, чтобы управляющие последовательности не попали в файл.

Если XMP не декодируется, поток извлекают отдельно, проверяют Filter и фактическую сигнатуру XML. Несоответствие заявленной кодировки байтам сохраняют как диагностический факт. Для безопасного разбора XML отключают внешние сущности. Исправленную копию метаданных создают только после сохранения исходного потока, а затем проверяют Subtype, Length и повторное чтение сохранённого PDF.

Справка команды pdfmetadata

Извлечение ресурсов командой pdfextract

pdfextract создаёт каталог и раскладывает выбранные ресурсы. Переключатели включают декодированные потоки, вложения, шрифты, JavaScript, метаданные и изображения. Если категории не заданы, команда извлекает широкий набор. Для контролируемой процедуры лучше перечислять нужные типы: структура выходного каталога тогда отражает цель, а неожиданное вложение или сценарий заметнее. Каждый результат получает контрольную сумму и безопасное имя.

Полученный файл не всегда имеет очевидное расширение. Тип определяют по сигнатуре, а не по имени из PDF. JavaScript рассматривают как непроверенный текст и не исполняют; бинарное вложение не открывают двойным щелчком; шрифт передают специализированному валидатору. Изображение может оставаться в DCT, JPX или JBIG2, поэтому не всякий поток готов к просмотру как PNG. FileSpec и место ссылки сохраняют вместе с извлечённым содержимым.

Команда умеет продолжать работу при отдельных ошибках zlib и PNG, чтобы один повреждённый ресурс не остановил всю выгрузку. Это полезно для анализа, но выход может быть неполным. Диагностику сохраняют, длину декодированных данных сравнивают вторым средством, а исходный поток не удаляют. Факт создания файла не подтверждает корректное декодирование каждого байта.

Параметры извлечения ресурсов pdfextract

Распаковка потоков через pdfdecompress

pdfdecompress создаёт копию без поддерживаемых фильтров сжатия у потоков. Файл обычно становится больше, зато операторы содержимого, сценарии и словари проще искать и сравнивать. Команда полезна, когда две страницы выглядят одинаково, но требуется понять различия в объектах, или когда строка скрыта внутри FlateDecode. Результат записывают под новым именем и повторно разбирают, не заменяя исходник.

DCTDecode, JPXDecode и JBIG2 не превращаются в несжатые растры. Это ожидаемое ограничение: JPEG, JPEG 2000 и JBIG2 требуют профильного декодера. Для каждого подозрительного объекта смотрят цепочку Filter. Если контентный поток с FlateDecode стал читаемым, проверяют операторы и ресурсы; если изображение осталось кодированным, извлекают его и анализируют в соответствующей среде.

Пересобранная копия может отличаться размером, порядком объектов и xref, поэтому побайтовое сравнение не показывает смысловое равенство. Сравнивают число страниц, каталоги, вложения, формы, шифрование и действия. Затем визуально проверяют безопасную копию в целевом просмотрщике. Обратное сжатие не обязано восстановить исходные байты, поэтому контрольная сумма оригинала остаётся основной точкой отсчёта.

Ограничения и параметры pdfdecompress

Шифрование командой pdfencrypt

pdfencrypt устанавливает парольную защиту и предлагает RC4 или AES с выбором размера ключа. По справке базовым режимом служит AES-128, а hardened включает усиленные параметры AES-256. Выбор зависит от требований получателя: старый просмотрщик может не открыть современную схему, но слабый RC4 нельзя выбирать автоматически только ради совместимости. Решение документируют и проверяют на целевой системе.

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

Проверка результата включает отказ открытия без пароля, успешное чтение с верным паролем, сохранение числа страниц и ключевых объектов. Также проверяют метаданные, вложения и формы, потому что ожидание пользователя о защите может не совпадать с фактическим поведением конкретного просмотрщика. Шифрование PDF не заменяет контроль доступа к исходнику и ключу.

Параметры шифрования pdfencrypt

Снятие шифрования через pdfdecrypt

pdfdecrypt читает защищённый документ, использует пароль и записывает незашифрованную копию. Поддерживаются распознаваемые библиотекой RC4 40–128 бит, AES-128 и AES-256. Неверный пароль или неизвестный обработчик безопасности должны считаться ошибкой даже при появлении выходного файла. Проверяют код завершения, журнал, сигнатуру и повторное чтение результата.

Расшифрованная копия требует более строгого хранения, чем вход. Каталог ограничивают правами, имя не содержит пароль, а временный результат не оставляют в общей папке. Если задача состоит только в чтении метаданных или извлечении ресурсов, оценивают возможность передать пароль соответствующей команде без долговременного сохранения открытого документа.

Ошибка может быть вызвана не паролем, а повреждённым Encrypt-словарём, trailer ID, xref или несовместимой схемой. В этом случае фиксируют параметры защиты и сравнивают поведение с QPDF либо другим парсером. Многократное пересохранение разными программами может скрыть первоначальную структуру, поэтому ремонт выполняют только на производной копии.

Параметры снятия шифрования pdfdecrypt

Проверка опасного содержимого через pdfcop

pdfcop применяет эвристические проверки и политику none, standard, strong или paranoid. Стандартная политика подходит для первичного отбора, строгие уровни увеличивают число отклонений и требуют ручной интерпретации. Зелёный результат не доказывает безопасность, а красный не доказывает вредоносность: команда указывает на конструкции и нарушения политики, но не воспроизводит полный рендерер и среду выполнения Acrobat JavaScript.

Конфигурационный файл меняет набор проверок, параметр перемещения складывает отклонённые документы в отдельный каталог, а журнал можно писать без цвета. Перемещение применяют только после фиксации исходных путей и контрольных сумм. Папка отклонённых файлов — очередь анализа, а не подтверждённый набор вредоносных образцов. Для каждой политики заранее определяют порядок ручной проверки.

После pdfcop отдельно ищут OpenAction, AA, JavaScript, Launch, URI, SubmitForm, вложения и формы. Извлечённые ресурсы проверяют антивирусом и специализированными анализаторами, а динамическое поведение наблюдают в изолированной среде. Пароль для защищённого PDF не записывают в общий журнал. Такой многоступенчатый процесс уменьшает риск чрезмерного доверия одной эвристике.

Уменьшение проблемного файла через pdfexplode

pdfexplode строит варианты документа, удаляя страницы или ресурсы по диапазону. Главная задача — локализовать причину сбоя после фаззинга или при нестабильном PDF. Вместо случайного ручного редактирования создают контролируемую серию и проверяют, в каком варианте ошибка просмотрщика или парсера исчезает. Для каждого результата фиксируют удалённый элемент, контрольную сумму и исход теста.

Диапазоны задаются выражениями вроде 5, 1-3 или 2-. Сначала синтаксис проверяют на маленьком документе. Удаление ресурсов помогает отделить проблемный шрифт, изображение, XObject или поток. Параллельные тесты запускают с одинаковой версией программы и одинаковыми лимитами, иначе различие среды можно ошибочно принять за эффект удалённого объекта.

Уменьшенный файл не является исправленным документом: исчезновение сбоя может сопровождаться потерей страницы или содержимого. Цель — минимальное условие воспроизведения. Найденный объект извлекают, проверяют длину, фильтры, числовые параметры и ссылки, после чего корректное исправление подтверждают на полном файле и в нескольких читателях.

Параметры уменьшения документа pdfexplode

Преобразование PDF в Ruby-сценарий

pdf2ruby создаёт Ruby-код, который пытается собрать эквивалентный документ средствами Origami. Такой код полезен как читаемая модель объектной структуры и основа минимального генератора теста. Команда прямо отмечена как экспериментальная, поэтому результат не заменяет исходник. Сценарий сначала читают как непроверенные входные данные, затем запускают без доступа к важным каталогам и сети.

Большие потоки, необычные строки и активные действия могут попасть в созданный код. Параметр исключения данных потоков уменьшает объём, но получившийся PDF уже не обязан воспроизводить страницы или вложения полностью. Перед запуском проверяют операции записи, пути и содержимое строк. После сборки сравнивают число страниц, объектов, вложения, формы, шифрование и список действий.

Практическая ценность особенно заметна при минимизации. Из сценария по одному удаляют несущественные объекты, каждый раз создают новый PDF и проверяют дефект. Получается воспроизводимый тест без ручной правки смещений и xref. Все отличия от исходника документируют, а не скрывают дополнительным пересохранением.

Справка экспериментальной команды pdf2ruby

Ограничения преобразования через pdf2pdfa

pdf2pdfa меняет документ так, чтобы Adobe Reader применял поведение, ориентированное на долговременное отображение: мультимедиа и JavaScript отключаются. Команда принимает вход и имя результата, но не является полным валидатором PDF/A. Соответствие профилю требует отдельной проверки шрифтов, цветового профиля, XMP, запрещённых объектов и других правил.

Рабочая процедура состоит из инвентаризации активного содержимого, создания копии, независимой валидации и визуального сравнения. Если форма зависит от вычисляющего сценария, а документ содержит видео или интерактивные элементы, результат может изменить назначение. До преобразования определяют, допустима ли потеря интерактивности, и сохраняют оригинал отдельно.

Успешная запись подтверждает только завершение команды. Она не гарантирует встраивание шрифтов, корректный OutputIntent или точное совпадение внешнего вида. Для долговременного хранения фиксируют профиль, отчёт валидатора, контрольную сумму и программу проверки. Ошибки устраняют по конкретным требованиям, а не повторным запуском той же операции.

Параметры преобразования pdf2pdfa

Интерактивная оболочка pdfsh

pdfsh запускает IRB внутри пространства имён Origami. В оболочке можно открыть документ, узнать число страниц, найти объект, проверить класс значения и сохранить изменённую копию. Это удобно на этапе исследования, когда последовательность запросов ещё меняется. После того как процедура понятна, команды переносят в отдельный Ruby-файл, чтобы получить повторяемый запуск и возможность проверить изменения в системе контроля версий.

Вход открывают из каталога только для чтения, а результат сохраняют под новым именем. Большой бинарный поток не печатают целиком в терминал: сначала выводят reference, класс, ключи и заявленную длину, затем записывают данные в отдельный файл. История IRB не должна содержать пароли, конфиденциальные пути и фрагменты документа. Ошибку интерактивной команды воспроизводят коротким сценарием.

Любое изменение действует на объект в памяти, а save сериализует текущее состояние. Поэтому перед сохранением повторно проверяют каталог, Pages, число страниц и изменённые ключи. После записи файл открывают новым PDF.read, а не продолжают доверять старому объекту. Это отделяет корректную структуру на диске от состояния интерактивной сессии.

Пример работы в интерактивной оболочке pdfsh

Визуальное исследование структуры

Для древовидного просмотра используют PDF Walker — отдельный графический интерфейс на базе Origami. Он показывает косвенные объекты, ссылки, xref и потоки, поддерживает поиск по регулярному выражению, переход к номеру объекта, декодирование и сохранение потока. Окно помогает выбрать интересующий узел, но окончательные изменения лучше оформлять сценарием, чтобы процедура повторялась на других файлах.

PDF Walker устанавливается отдельно и зависит от графических Ruby-компонентов GTK. Если он не запускается в новой системе, команды Origami продолжают работать: можно извлечь ресурсы, метаданные, выполнить проверку и открыть pdfsh. Это полезно учитывать при развёртывании на сервере без дисплея. Найденный в дереве объект проверяют по входящим и исходящим ссылкам, а не только по подписи в интерфейсе.

При поиске активного содержимого начинают с Catalog, OpenAction, Names и AcroForm, затем проверяют AA у страниц, аннотаций и полей. Поиск слова JavaScript не покрывает обфускацию, объектные потоки и косвенные ссылки. Декодированный поток сохраняют рядом с описанием исходного объекта, Filter и контрольной суммой, чтобы не потерять связь между отображаемым текстом и байтами PDF.

Окно PDF Walker для просмотра структуры через Origami

Создание нового PDF в Ruby-коде

Новый документ создают объектом PDF, добавляют страницу, записывают содержимое и вызывают save. В короткой форме PDF.write принимает блок и сохраняет файл после его выполнения. Такой путь подходит для тестовых документов, воспроизводимых образцов ошибки и отчётов с заранее известной структурой. Он не заменяет систему макетирования: координаты, размеры, шрифты и ресурсы контролируются программно.

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

Для сравнимого результата фиксируют входные данные и учитывают поля, которые закономерно меняются: даты, идентификатор трейлера и порядок объектов. Успешный вызов save ещё не гарантирует, что другой читатель примет файл. Минимальный тест включает повторный разбор Origami, строгий структурный валидатор и визуальное отображение безопасного документа.

Изменение существующих объектов

После PDF.read сценарий находит словарь или поток по структурному пути от каталога к Pages, AcroForm, Names или Metadata. Поиск только по номеру объекта хрупок: номера меняются после пересборки и отличаются у похожих документов. Если номер используется в расследовании, рядом фиксируют тип, поколение, ключи и цепочку ссылок. Массовый сценарий проверяет структуру, а неподходящий файл пропускает с понятным статусом.

Новый ключ должен иметь тип, разрешённый спецификацией. Строка, Name, число, массив и reference не взаимозаменяемы. Неверное значение может записаться без исключения, но вызвать неодинаковое поведение просмотрщиков. Перед изменением сохраняют старое представление, после — открывают результат заново и подтверждают класс. Для потоков полагаются на сериализатор, который корректно обновляет Length.

Удаление активного действия включает все ссылки на него. Удалить только JavaScript-поток недостаточно, если Action остаётся и указывает на отсутствующий объект. Сначала перечисляют входящие связи, затем удаляют или заменяют связанный узел. После очистки повторяют полный поиск Action, извлечение сценариев и pdfcop, а также проверяют, не нарушилась ли форма или навигация.

Потоки, фильтры и предикторы

Поток состоит из словаря параметров и байтов. Filter задаёт цепочку декодирования, DecodeParms — дополнительные настройки. Origami поддерживает фильтры с предикторами, поэтому может разбирать данные, подготовленные алгоритмами PNG- или TIFF-подобной постобработки. Порядок фильтров принципиален: его нельзя сортировать или применять выборочно без понимания результата.

Length бывает прямым числом или косвенной ссылкой. Несовпадение заявленной и фактической длины встречается в повреждённых и специально подготовленных файлах. Парсер может восстановить границы, но расхождение фиксируют. Для доказательной работы хранят исходные байты, декодированную копию, параметры и контрольные суммы. Частично извлечённый поток не заменяет оригинал.

Многократное сжатие и высокий коэффициент распаковки требуют ограничений памяти и времени. Процесс запускают с квотами, особенно для неизвестного файла. Перед декодированием сортируют потоки по заявленному размеру и журналируют номер объекта. Ошибка исчерпания памяти не доказывает вредоносность, но требует проверки размеров, циклических ссылок и аномальных DecodeParms в изолированной среде.

Объектные потоки и таблицы ссылок

Object streams объединяют несколько косвенных объектов внутри сжатого потока. Простой поиск строк по файлу может их не увидеть. Структурный парсер декодирует контейнер и восстанавливает отдельные объекты, поэтому действия и словари в такой упаковке становятся доступны обходу. При сравнении учитывают как номер контейнера, так и номер сжатого объекта, иначе ссылка в отчёте будет неоднозначной.

xref бывает классической таблицей или потоком. Инкрементальные обновления добавляют новые секции, и один номер объекта может иметь несколько исторических представлений. Анализ активного состояния идёт по цепочке trailer и Prev. Старое содержимое может оставаться в байтах после логического удаления; это важно для расследования, но обычно не отображается читателем.

Пересохранение меняет организацию объектов и часто удаляет недостижимые остатки. Поэтому побайтовая разница ожидаема, а исторический анализ выполняют до записи производной копии. Для логического сравнения строят списки активных объектов, страниц, действий и вложений. Если требуется очистить историю обновлений, результат проверяют отдельно и не выдают за неизменённый оригинал.

Формы AcroForm и XFA

Origami распознаёт AcroForm и XFA, поэтому можно получить поля, виджеты, значения и связанные действия. AcroForm хранит поля в объектной структуре PDF, XFA — XML-пакеты с собственной моделью данных и шаблоном. Перед изменением определяют, какая система управляет отображением. Гибридная форма может показывать XFA в одном просмотрщике и AcroForm в другом.

Изменение ключа V не всегда обновляет видимое значение. Читатель может использовать appearance stream или NeedAppearances. Для предсказуемого результата проверяют значение, внешний вид и поведение целевого приложения. Вычисляемые поля зависят от JavaScript, поэтому удаление сценариев может изменить итоговые суммы. Flatten выполняют специализированным средством, которое действительно рендерит внешний вид.

При выгрузке сохраняют полное имя поля, тип, флаги, значение, виджет и номер объекта. Одинаковые короткие имена могут находиться в разных ветвях. XFA извлекают как поток, проверяют кодировку и пространства имён, а XML разбирают без внешних сущностей. Поле подписи не заполняют как обычное текстовое: его ByteRange и криптографический контейнер требуют отдельного процесса.

Вложения и дерево имён

Вложения связываются с EmbeddedFiles в дереве Names или с аннотациями FileAttachment. pdfextract выгружает содержимое, а Ruby-код показывает FileSpec, отображаемое имя, описание и EmbeddedFile. Проверяют оба пути: поиск только аннотаций пропускает файлы из панели вложений, а поиск только дерева имён — вложение конкретной страницы. Для каждого файла фиксируют объект и место ссылки.

Имя из PDF не считается безопасным путём. Удаляют компоненты каталогов, управляющие символы и конфликтующие имена, тип определяют по сигнатуре, а к имени добавляют номер объекта или часть контрольной суммы. Исполняемые и сценарные форматы не запускают в основной системе. Службы миниатюр и индексации отключают, чтобы извлечённый ресурс не обработался автоматически.

Удаление вложения требует убрать FileSpec, ссылку из дерева или аннотации и затем пересобрать недостижимый поток. После сохранения снова запускают извлечение. Если исходное вложение должно сохраняться юридически, создают очищенную производную копию, а оригинал помещают в неизменяемое хранилище. Связь между ними фиксируют контрольными суммами и журналом операции.

JavaScript, действия и события

JavaScript может находиться в Names, OpenAction, AA документа и страниц, аннотациях, формах и полях. pdfextract даёт список сценариев, а объектный обход показывает событие вызова. Контекст важнее одного фрагмента текста: код при открытии документа оценивается иначе, чем действие после осознанного нажатия. Поток декодируют, но не выполняют в рабочем профиле.

Для эмуляции разработчик указывает дополнительный therubyracer. Такая эмуляция не повторяет полностью API и защитную модель конкретного Acrobat или другого читателя. Её используют для исследования выражений, но не как окончательное подтверждение безопасности. Запуск выполняют без сети, с лимитом времени и памяти. Неизвестные методы и исключения сохраняют в отчёте.

Очистка охватывает JavaScript, URI, Launch, SubmitForm и GoToR как разные типы действий. Удаление только JS оставляет другие внешние переходы. После изменения строят полный список Action до и после, проверяют входящие ссылки и визуальную функциональность формы. Отсутствие всплывающего окна при открытии не доказывает, что скрытых действий больше нет.

Цифровые подписи и Usage Rights

Библиотека поддерживает объекты цифровых подписей и Usage Rights, поэтому сценарий может прочитать словари, ByteRange и связь с полем формы. Любое изменение байтов способно изменить статус подписи. Подписанный файл сначала анализируют без сохранения, фиксируют контрольную сумму и диапазоны, а производную копию маркируют как изменённую. Нельзя обещать сохранение подписи после структурной пересборки.

Структурный разбор не заменяет криптографическую валидацию цепочки сертификатов, времени и статуса отзыва. Для неё нужен проверяющий инструмент с управляемым хранилищем доверия. Origami помогает увидеть контейнер, покрытые диапазоны и последующие обновления. Результат структурной проверки и результат криптографической проверки записывают раздельно.

Usage Rights разрешают дополнительные функции в Adobe Reader. Удаление соответствующего словаря или пересборка меняют поведение. Перед очисткой решают, допустима ли потеря прав и интерактивности. Выход проверяют в целевом читателе, а исходный подписанный документ сохраняют неизменным. Если подпись охватывает не весь файл, отдельно исследуют добавленные после неё секции.

Работа с FDF и PPKLite

Парсер заявляет чтение PDF, FDF и PPKLite. FDF переносит данные форм и ссылки на основной PDF, поэтому полезен при обмене заполненными полями. Его значения, действия и адрес связанного документа проверяют с той же осторожностью, что и AcroForm. Сначала определяют тип входа по заголовку, затем выбирают соответствующий класс, иначе ошибка может выглядеть как повреждение.

PPKLite относится к хранилищу сертификатов Adobe. Возможность разобрать структуру не означает доверия каждому сертификату. Содержимое сравнивают с утверждённым хранилищем и не импортируют в рабочий профиль без проверки. Парольные операции выполняют на копии и не записывают секреты в журнал. Для криптографического вывода нужен отдельный валидатор.

Общая объектная модель позволяет сопоставить поле FDF, подпись PDF и сертификат, но обязательные элементы форматов различаются. Сценарий проверяет типы и отсутствие неожиданных действий. При преобразовании данные форм не должны молча теряться: после записи сравнивают полные имена полей, значения, кодировки и ссылки на исходный документ.

Ошибки разбора и повреждённые документы

Сообщение invalid xref означает, что смещение, номер объекта или цепочка обновлений не согласованы. Перед ремонтом проверяют заголовок объекта по указанному смещению, trailer и Prev. Другой парсер может восстановить файл эвристически, но это меняет доказательную картину. Исходные байты сохраняют, а восстановленную копию маркируют и проверяют отдельно.

Ошибка FlateDecode требует проверки Length, порядка Filter и DecodeParms. Если pdfextract продолжил работу с игнорированием ошибок, поток может быть обрезан. Для подтверждения применяют второй декодер и сравнивают длину. Проблемы предиктора часто связаны с Columns, Colors или BitsPerComponent. Диагностику привязывают к reference, а не только к имени выходного файла.

Рекурсивный обход обязан учитывать циклы: PDF — граф, а не чистое дерево. Набор посещённых references и ограничение глубины предотвращают бесконечный переход через Parent, Pages и поля. Для неизвестного входа также задают предел числа объектов, размера потока и времени. Частично сохранённый результат после исключения не передают дальше без повторного чтения.

Совместимость Ruby и зависимостей

Минимальное требование не гарантирует одинаковое поведение на каждом новом Ruby. Стандартные библиотеки меняют упаковку, проверка аргументов становится строже, а нативные зависимости GTK могут не собраться. Для рабочего конвейера фиксируют версию Ruby, список gem и образ среды. При обновлении прогоняют набор эталонных PDF с шифрованием, объектными потоками, формами и повреждениями.

cannot load such file относится к модулю Ruby, а не к PDF. Читают имя, проверяют gem environment и путь исполняемого файла. Системный пакет и пользовательский gem могут использовать разные библиотеки. which, ruby -v и gem which помогают найти расхождение. Повторная установка без очистки PATH часто создаёт ещё одну копию и усложняет диагностику.

Дополнительный JavaScript-движок увеличивает поверхность атаки и может требовать нативной сборки. Если нужно только извлечь сценарии, он не нужен. Графический PDF Walker также не обязателен для команд. На сервере разумно держать минимальную среду без GUI, а визуальный анализ выполнять в отдельной рабочей станции с проверенной комбинацией GTK и Ruby.

Практические сценарии работы

Проверка PDF из электронной почты

Сначала вычисляют контрольную сумму вложения и помещают копию в каталог без автопросмотра. pdfcop запускают со стандартной политикой и журналом без цвета, pdfmetadata сохраняют в JSON, а pdfextract выгружает JavaScript и вложения. При обнаружении OpenAction или AA фиксируют объект, событие и цепочку ссылок. До завершения проверки файл не открывают обычным читателем.

Решение принимают после анализа извлечённых файлов, списка действий и поведения в песочнице. Отчёт pdfcop не заменяет антивирус. Если документ зашифрован, пароль вводят в контролируемой сессии и не сохраняют в истории. В журнал включают контрольную сумму, команды, коды завершения и имена результатов без секретов.

Поиск скрытого JavaScript

Ключ извлечения сценариев даёт начальный список, затем Ruby-код обходит Catalog, OpenAction, Names, AA страниц, аннотации и поля. Для каждого действия сохраняют тип, событие и reference. Потоки декодируют с учётом Filter. Строковый поиск дополняют разбором object streams и косвенных ссылок, потому что слово JavaScript может отсутствовать в открытом виде.

После очистки повторяют полный обход и отдельно ищут URI, Launch, SubmitForm и GoToR. Сравнивают список действий до и после. Визуальное отсутствие окна не считается проверкой. Если сценарий обеспечивал вычисление формы, владельцу процесса показывают, какая функция потеряна, и предлагают безопасную замену.

Извлечение вложений для расследования

pdfextract запускают с отдельной категорией вложений и новым каталогом. Для каждого файла определяют сигнатуру, размер и контрольную сумму, безопасно нормализуют имя. Через FileSpec устанавливают отображаемое имя, описание и место ссылки. Одинаковые имена различают номером объекта, чтобы один результат не перезаписал другой.

Исполняемые форматы отправляют в отдельную среду, не на рабочий компьютер. Для удаления создают производную копию, убирают ссылки из EmbeddedFiles или аннотации и повторным извлечением подтверждают отсутствие. Исходник и первичные вложения сохраняют неизменными, если этого требует расследование.

Сравнение двух визуально одинаковых документов

Оба PDF декомпрессируют в отдельные копии, извлекают метаданные и строят инвентарь страниц, форм, вложений, шифрования и действий. Побайтовая разница ожидаема из-за порядка объектов и xref. Основное сравнение идёт по активной объектной модели, цепочке обновлений и потокам содержимого страниц.

Если различие найдено в контентном потоке, сравнивают операторы и используемые ресурсы. Разница только в номерах объектов может быть несущественной; отличие в OpenAction, аннотации или EmbeddedFiles существенно даже при одинаковой картинке. Итог связывают с контрольными суммами обоих входов.

Локализация сбоя просмотрщика

pdfexplode создаёт варианты по страницам и ресурсам. Каждый запускают в одинаковой изолированной среде, фиксируя, исчезает ли сбой. Деление диапазона пополам быстро локализует страницу, затем ресурсное уменьшение отделяет шрифт, изображение, XObject или поток. Результаты записывают в таблицу, не полагаясь на память.

Найденный ресурс анализируют профильным средством. Минимальный PDF не считают исправленным: он лишь воспроизводит условие. Конфиденциальные данные удаляют перед передачей разработчику, но проверяют, что дефект сохранился. Корректное исправление тестируют на полном документе и нескольких версиях целевого читателя.

Снятие пароля для внутреннего процесса

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

После завершения незашифрованный файл удаляют или переносят в защищённое хранилище. Ошибку не сводят автоматически к неверному паролю: проверяют Encrypt, trailer ID и xref. Любую восстановленную копию отделяют от исходника и снабжают собственной контрольной суммой.

Установка защиты перед передачей

Выбирают AES и параметры, поддерживаемые получателем. Создают новый файл, проверяют отказ без пароля и успешное открытие с ним. Правильный пароль передают по другому каналу. Метаданные и имена вложений просматривают отдельно: парольная защита не должна создавать ложное ожидание, что никакие вспомогательные сведения не видны.

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

Пакетный сбор метаданных

Для каждого файла вызывают pdfmetadata с JSON, добавляют размер и SHA-256. Ошибка одного PDF не останавливает очередь: статус, сообщение и путь записывают отдельно. Даты нормализуют в отдельном столбце, сохраняя исходную строку. Цвет терминала отключают, чтобы в JSON не попали управляющие коды.

По таблице находят несогласованные Creator и Producer, отсутствующий XMP, даты в будущем и повторяющиеся шаблоны. Это критерии отбора, а не доказательство подделки. Выбранные документы затем разбирают на уровне объектов и сравнивают с известной цепочкой обработки.

Создание минимального тестового PDF

Сценарий создаёт PDF, добавляет страницу и одну строку, сохраняет и читает обратно. Такой тест отделяет проблему среды от дефекта конкретного файла. Затем по одному добавляют шрифт, изображение, форму или действие, каждый раз фиксируя контрольную сумму и результат в валидаторе.

Если ошибка появляется после конкретного шага, минимальный код становится воспроизводимым примером. Из него убирают личные данные и нестабильные даты. В отчёт включают Ruby, набор gem и точную операцию, чтобы другой специалист получил тот же объектный граф.

Удаление действия при открытии

Сценарий находит OpenAction, определяет тип и все ссылки на объект. Затем создаёт копию без этой связи. Если действие используется ещё где-то, его не удаляют до проверки других владельцев. Для JavaScript также проверяют Names и AA, чтобы не оставить альтернативный путь запуска.

После записи выполняют pdfcop, pdfextract для сценариев и полный обход Action. Визуально сравнивают страницы и форму: действие могло инициализировать поля или навигацию. Если функция нужна, проектируют замену, а не возвращают исходный опасный механизм.

Проверка встроенных шрифтов

pdfextract сохраняет шрифтовые потоки. Через Resources сопоставляют имя на странице, FontDescriptor, подтип и файл. Учитывают наследование ресурсов от дерева Pages. Отсутствующий шрифт может изменить внешний вид, а повреждённый — вызвать сбой декодера. Расширение при извлечении не считают доказательством формата.

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

Изучение инкрементальных обновлений

Ruby-код проходит цепочку trailer и Prev, отмечает xref-секции и версии объектов. Так видно, какие значения заменены без полной перезаписи. Старый объект может сохранять удалённую строку, действие или вложение, хотя активная версия его не использует. Для каждого состояния различают исторический остаток и текущую ссылку.

Обычная очищенная копия может быть пересобрана без недостижимых объектов. Для расследования это делают только после сохранения оригинала, потому что история исчезнет. В отчёте отдельно перечисляют активное содержимое и остатки прошлых обновлений, не смешивая их влияние на читатель.

Проверка формы перед заполнением

Перечисляют поля AcroForm, полные имена, типы, флаги, значения, виджеты и действия. Отдельно отмечают поля подписи, вычисляемые поля и XFA. Тестовое заполнение проводят на одном образце. Затем проверяют V, appearance stream и отображение в целевом приложении.

Если значение в объекте изменилось, а поле выглядит пустым, операция не завершена. Формируют внешний вид поддерживаемым способом или используют средство с рендерингом. Поля с одинаковыми короткими именами адресуют полным путём. Удаление JavaScript согласуют с зависимыми вычислениями.

Разбор XMP с ошибочной кодировкой

Metadata-поток извлекают без изменения исходника, сохраняют Filter, Length и контрольную сумму. XML разбирают с запрещёнными внешними сущностями. Если декларация кодировки не соответствует байтам, создают нормализованную аналитическую копию, не перезаписывая исходный поток.

Значения сравнивают со словарём Info, но не объединяют автоматически. При исправлении XMP проверяют Subtype XML, новую Length и повторное чтение. Дата или автор из одного слоя не должны молча заменять другое значение, потому что расхождение может быть важным.

Обработка большого PDF

Документ открывают с lazy: true и обращаются только к нужным ветвям. Перед декодированием крупного потока журналируют reference и размер. Процесс ограничивают по памяти, времени и числу объектов. Ресурсы выгружают по категориям, а не печатают в терминал.

Неожиданный коэффициент распаковки рассматривают как риск исчерпания ресурсов. Параллельность ограничивают общей памятью, каждому процессу дают отдельный каталог. Если один объект вызывает остановку, его исследуют отдельно, а не перезапускают весь файл без лимитов.

Подготовка читаемой структурной копии

pdfdecompress создаёт вариант, где поддерживаемые потоки доступны поиску. Его повторно разбирают и сравнивают страницы, действия, вложения и формы. Копию используют для анализа, но не считают автоматически безопасной и не заменяют оригинал.

DCT, JPX и JBIG2 остаются кодированными. Для полной инвентаризации изображений применяют pdfextract и профильные декодеры. Если декомпрессия изменила поведение читателя, фиксируют конкретный объект и не делают вывод только по размеру файла.

Создание Ruby-модели существующего PDF

pdf2ruby выводит сценарий. Код просматривают перед запуском, большие потоки при необходимости исключают. Созданный PDF сравнивают по страницам, объектам, метаданным, шифрованию и действиям. Экспериментальный статус означает, что несовпадение нужно ожидать и документировать.

Сценарий полезен для теста и обучения объектной модели, но не для сохранения доказательно идентичных байтов. Запуск ограничивают правами, путями и сетью. Любую разницу в активном содержимом рассматривают отдельно, даже если страницы визуально совпали.

Проверка PDF/A-ориентированной копии

После pdf2pdfa запускают профильный валидатор. Проверяют шрифты, OutputIntent, XMP, запрещённые действия и визуальный результат. Команда отключает мультимедиа и JavaScript в Adobe Reader, но не подтверждает весь профиль PDF/A.

Если документ зависит от интерактивности, статическую копию маркируют как производную. Ошибки валидатора устраняют по конкретным правилам. В хранилище сохраняют контрольную сумму, выбранный профиль и отчёт проверки, чтобы последующая система знала, что именно было подтверждено.

Автоматическая сортировка pdfcop

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

Строгая политика может отклонить легитимную форму, а стандартная пропустить неизвестную технику. Поэтому статус означает соответствие выбранным правилам, а не окончательный диагноз. Изменение конфигурации сопровождают повторным тестом на известных безопасных и опасных образцах.

Проверка результата после изменения

Выходной PDF снова читают новым PDF.read. Сравнивают страницы, ресурсы, метаданные, шифрование, формы, подписи, вложения и действия по чек-листу задачи. Один просмотрщик недостаточен: нужен структурный валидатор и целевое приложение.

Контрольную сумму связывают с версией сценария и параметрами. Повторный запуск на том же исходнике должен давать логически одинаковый документ; закономерные различия дат и ID учитывают отдельно. Частичный файл после исключения не публикуют, даже если он открывается.

Сравнение Origami PDF с аналогами

ПрограммаЛучше подходит дляГлавное ограничение
Origami PDFОбъектного анализа, генерации и Ruby-автоматизацииНет визуального редактирования страниц
PDF CommanderПравки текста и страниц через графический интерфейсНе предназначен для низкоуровневого разбора объектов
QPDFСтруктурных преобразований, шифрования, разделения и объединенияНе извлекает текст и не редактирует видимое содержимое
pikepdfPython-сценариев для чтения, ремонта и преобразования через QPDFТребует программирования на Python
peepdfИнтерактивного поиска подозрительных объектов и сценариевОсновной фокус — исследование, а не правка страниц
PDF WalkerДревовидного просмотра объектов OrigamiНужны отдельные GTK-зависимости

Для обычной правки текста и страниц рациональнее PDF Commander. Для стабильных структурных преобразований без Ruby подходит QPDF, разработчикам на Python — pikepdf. peepdf удобен при интерактивном исследовании подозрительного файла, а PDF Walker — для визуального дерева объектов. Origami PDF выбирают, когда нужен Ruby API, генерация собственных PDF и сочетание готовых команд с программируемым обходом.

Функциональные ограничения

Origami не рендерит страницы и не даёт мышью выбирать текст, двигать блоки или ставить пометки. Координатное содержимое можно менять кодом, но внешний вид проверяется другим приложением. Для простой опечатки структурный сценарий обычно сложнее редактора. Отсутствие рендеринга также означает, что программа не выполняет OCR и не извлекает визуальный текст со скана.

pdfcop является эвристическим фильтром, pdf2ruby — экспериментальным преобразованием, а pdf2pdfa — не полной сертификацией PDF/A. Эти границы нельзя закрыть одним успешным кодом завершения. Графический PDF Walker устанавливается отдельно. Для криптографической подписи, PDF/A и динамического поведения нужны профильные средства с собственной моделью доверия.

Интерфейс рассчитан на команды и Ruby-код, русской локализации нет. Пользователю приходится понимать PDF-объекты, ссылки и фильтры. Ошибка типа или удаление общей ссылки может дать файл, который один читатель восстанавливает, а другой отвергает. Поэтому повторное чтение, валидация и тестирование входят в каждую операцию, а не выполняются только в конце крупного проекта.

Диагностика типичных проблем

Команда не найдена

Проверяют gem bindir, PATH, which и активный Ruby. Системный пакет и пользовательский gem могут ставить исполняемые файлы в разные каталоги. Не устанавливают ещё одну копию, пока не выяснено, откуда оболочка загружает текущую команду.

LoadError при старте

Читают имя отсутствующего модуля. Для новых Ruby отдельной установки могут потребовать matrix или rexml. Модуль ставят в тот же gemset, затем проверяют gem which и справку. Ошибка загрузки не относится к структуре входного PDF.

invalid xref

Сохраняют исходник, проверяют trailer, Prev и смещения. Сравнивают вывод другого парсера. Восстановленную копию маркируют, потому что пересборка может удалить недостижимые объекты и историю обновлений.

Не декодируется поток

Фиксируют reference, Filter, DecodeParms и Length. Проверяют порядок фильтров, применяют второй декодер, сравнивают длину. Файл, извлечённый с игнорированием ошибок, может быть частичным.

Форма выглядит пустой

Проверяют V, appearance stream, NeedAppearances и XFA. Одного изменения значения недостаточно. Результат тестируют в целевом читателе и при необходимости формируют внешний вид средством с рендерингом.

Подпись стала недействительной

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

Изображение не открывается

Определяют сигнатуру и Filter. DCT, JPX и JBIG2 требуют своих декодеров. Проверяют Width, Height, ColorSpace и BitsPerComponent; расширение выходного файла не считается гарантией формата.

Слишком много срабатываний pdfcop

Уточняют политику и конфигурацию, разбирают категории. Легитимная интерактивная форма может нарушить строгую политику. Снижение строгости допускается только вместе с ручным анализом.

Файл исчерпывает память

Используют lazy, лимиты, последовательную выгрузку и ограниченную глубину. Журналируют объект перед декодированием. Не снимают ограничения ради одного запуска неизвестного потока.

Сценарий работает только на одном файле

Убирают фиксированные номера объектов, проверяют типы и наличие ключей, учитывают наследование и ссылки. Неподходящий файл должен получить контролируемый статус, а не частично записанный результат.

Безопасная организация анализа

Рабочий каталог делят на вход, результаты, извлечённые ресурсы и журналы. Вход доступен только для чтения, выходные имена формируются из безопасного идентификатора. Процесс запускают от отдельной учётной записи без доступа к личным файлам, ключам и сети. Автоматические миниатюры и индексаторы отключают для каталога извлечения.

Лимиты памяти, времени, числа объектов и размера результата защищают от специально подготовленных потоков. JavaScript, шрифты и вложения считаются непроверенными. Их тип устанавливают по сигнатуре, обработку выполняют отдельными инструментами. Пароль не попадает в историю, общий журнал или имя файла.

Журнал содержит SHA-256 входа, команды и параметры без секретов, код завершения, список результатов и их контрольные суммы. Также указывают, какие категории проверены и какие не проверялись. Это не даёт превратить успешную утилиту в чрезмерное заключение о полной безопасности или полном соответствии стандарту.

Инвентаризация активных действий перед изменением

Перед очисткой документа составляют полный список точек запуска. Проверяют Catalog/OpenAction, дополнительные действия AA у каталога, страниц, аннотаций и полей формы, элементы Names/JavaScript, действия URI, Launch, GoToR, SubmitForm и ImportData. В Origami каждый найденный словарь связывают с номером косвенного объекта и путём от корня, потому что одинаковый сценарий может быть доступен из нескольких мест. Простое удаление первого ключа OpenAction не устраняет действия, назначенные кнопке или событию страницы.

Инвентаризацию сохраняют отдельно от изменённого PDF. Для каждого действия указывают тип S, целевой объект, наличие вложенного Next и место ссылки. Цепочка Next может продолжать выполнение после безобидного перехода, поэтому её обходят с контролем уже посещённых ссылок. Строки JavaScript извлекают как байты, не запускают и не нормализуют до подсчёта контрольной суммы. Если сценарий хранится в потоке, дополнительно фиксируют фильтр и длину после декодирования.

Очистку выполняют на копии и по заранее утверждённому правилу. Удаление действия не должно случайно удалять видимую аннотацию, значение поля или весь словарь страницы. После изменения повторяют тот же инвентаризатор и сравнивают списки: ожидаемые записи должны исчезнуть, остальные структуры — сохраниться. Затем файл перечитывают Origami и независимым парсером, а визуальное сравнение проводят в изолированном просмотрщике без исполнения активного содержимого.

Наследуемые ресурсы и дерево страниц

Страница PDF может не содержать собственные MediaBox, CropBox, Rotate или Resources: значения наследуются от предков в дереве Pages. При анализе Origami нельзя делать вывод по одному словарю Page. Сначала проходят цепочку Parent до ближайшего определённого ключа и записывают эффективное значение. Это особенно важно при поиске шрифтов и XObject, потому что ресурс, используемый десятками страниц, может быть объявлен один раз на уровне общего узла.

Перед изменением общего словаря считают, сколько страниц его наследует. Замена шрифта или удаление имени XObject в родительском Resources может повредить все дочерние страницы, хотя тестировалась только одна. Безопасный приём — скопировать нужный словарь в конкретную страницу, изменить копию и проверить, что ссылки других страниц остались прежними. При копировании учитывают вложенные ProcSet, ColorSpace, Pattern, Shading, ExtGState и Properties, а не только Font и XObject.

Дерево Pages также проверяют на согласованность Kids и Count. Число Count у промежуточного узла должно соответствовать количеству листьев в его поддереве, но повреждённый файл может содержать неверное значение или цикл. Обход ограничивают глубиной и множеством посещённых references. После удаления или перестановки страниц пересчитывают Count по фактическому дереву и повторно открывают документ, чтобы ошибка не маскировалась кешем текущего объекта Ruby.

Аннотации, поля и невидимые области страницы

Список Annots содержит не только заметки и выделения. В нём встречаются ссылки, виджеты формы, вложения, мультимедиа, печати, штампы и служебные элементы. Для каждого словаря Origami проверяют Subtype, Rect, F, Contents, AP и действие A. Прямоугольник может лежать за пределами CropBox, иметь нулевую площадь или перекрывать почти всю страницу прозрачной областью. Поэтому отсутствие видимого значка не означает отсутствия интерактивного объекта.

Флаги аннотации определяют печать, видимость, блокировку и поведение при масштабировании. Их разбирают побитово, а не сравнивают с одним ожидаемым числом. Виджет связывают с полем через Parent или Kids; значение V может находиться у родителя, а внешний вид — в AP самого виджета. При удалении поля проверяют обе стороны связи, иначе в AcroForm останется недостижимый элемент или на странице сохранится виджет без логического поля.

Для отчёта полезно сопоставить координаты Rect с эффективными MediaBox и CropBox страницы. Так выявляются кликабельные зоны за видимой областью, ошибочные координаты после поворота и аннотации на страницах нестандартного размера. Origami даёт структурные числа, но не рассчитывает окончательный пиксельный вид, поэтому спорные случаи подтверждают рендерером. Изменённый файл проверяют при включённой и отключённой печати аннотаций, если документ предназначен для вывода на бумагу.

Строки, имена и кодировки без потери байтов

Строковый объект PDF может быть записан в круглых скобках с экранированием или в шестнадцатеричном виде. Текст часто использует PDFDocEncoding либо UTF-16BE с меткой порядка байтов, но произвольная строка не обязана быть человеческим текстом. При чтении Origami сохраняют исходные байты для контрольной суммы и отдельно формируют отображаемое представление. Принудительное преобразование каждой строки в UTF-8 может испортить идентификаторы, зашифрованные данные и нестандартные значения.

Имена PDF начинаются с косой черты и допускают шестнадцатеричные escape-последовательности. Два визуально похожих ключа после неосторожной декодировки могут оказаться разными байтовыми именами. При поиске опасных действий сравнивают нормализованное значение класса Name, а в отчёте при необходимости показывают и исходное представление. Новый ключ создают объектом нужного типа, не подставляя строку Ruby туда, где сериализатор ожидает PDF Name.

Проблемы кодировки особенно заметны в метаданных, закладках, именах вложений и значениях форм. Исправление выполняют только после определения слоя: Info, XMP, outline, FileSpec или AcroForm. Замена текста в одном месте не синхронизирует остальные. После сохранения проверяют повторное декодирование, длину строки, отсутствие незапланированной подстановки символов и отображение в целевом читателе. Для юридически значимых файлов оригинальные байты сохраняют как отдельное доказательство.

Ограничение памяти и защита от чрезмерной распаковки

Длина сжатого потока не показывает размер после декодирования. Небольшой объект FlateDecode может развернуться в сотни мегабайт, а цепочка фильтров — потребовать несколько промежуточных буферов. Перед массовым pdfdecompress или извлечением через Origami устанавливают лимиты времени, памяти, количества объектов и суммарного объёма результатов. Неизвестный документ обрабатывают в отдельном процессе, чтобы его завершение не разрушило основную очередь.

Сценарий сначала перечисляет streams без декодирования, сортирует их по заявленной Length и отмечает необычные Filter и DecodeParms. Затем данные раскрывают по одному объекту, сразу записывают на диск и освобождают ссылки Ruby. Если библиотечный метод возвращает целую строку в память, лимит внешнего процесса остаётся обязательным. Объект, превысивший порог, получает отдельный статус; его не пропускают молча и не считают безопасным.

Для изображений проверяют Width, Height, BitsPerComponent и число компонентов ещё до полноценного декодирования. Произведение параметров даёт приблизительный размер растра и помогает обнаружить нереалистичные значения. Для predictor-потоков учитывают Columns и Colors, иначе оценка будет ошибочной. После остановки по лимиту сохраняют reference и параметры, чтобы исследование можно было продолжить специализированным декодером в более жёсткой песочнице.

Потоки ввода, вывода и безопасные конвейеры

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

Имена входных файлов передают отдельными аргументами, чтобы оболочка не интерпретировала специальные символы. Выход создают во временном каталоге, после успешного повторного чтения атомарно переименовывают. При пакетной обработке один повреждённый документ не должен перезаписать хороший результат предыдущего запуска или остановить журналирование остальных элементов очереди.

Контрольная сумма вычисляется до передачи файла команде и после появления результата. В журнале сохраняют точный массив аргументов без паролей, версию Ruby, код завершения и размер stdout/stderr. Для больших наборов полезен режим без интерактивных цветов. Если команда поддерживает каталог назначения, заранее проверяют, что она не следует по символическим ссылкам за пределы рабочей области и не создаёт имена из непроверенных путей вложений.

Сохранение, сериализация и повторное чтение

Изменение Ruby-объекта ещё не означает корректный PDF на диске. Во время save библиотека сериализует словари и потоки, вычисляет длины, формирует xref и trailer. Ошибка может проявиться только при записи или при следующем чтении. Поэтому результат сначала сохраняют во временный файл, проверяют ненулевой размер и заголовок, затем открывают новым вызовом PDF.read. Текущий объект в памяти не используют как доказательство корректности сериализации.

После повторного чтения сверяют число страниц, корневой каталог, Info, Encrypt, ID, формы, вложения и те objects, которые должны были измениться. Внешний инструмент проверяет xref и синтаксис. Если операция затрагивала поток, сравнивают его декодированное содержимое и параметры фильтра. Если менялись страницы, дополнительно рендерят контрольные листы. Успешное открытие одним терпимым просмотрщиком недостаточно: он мог автоматически восстановить ошибку.

Побайтовое различие ожидаемо даже при малом логическом изменении: может поменяться порядок объектов, пробелы, представление строк и таблица ссылок. Поэтому критерии приёмки задают на уровне структуры и внешнего вида. Исходную подпись не считают сохранённой после полной пересериализации без отдельной криптографической проверки. Оригинал, рабочую копию и итог связывают контрольными суммами и кратким описанием операции.

Логическое сравнение двух документов

Для сравнения генераторов или результатов преобразования строят канонический отчёт. В него включают число страниц, размеры и поворот, список шрифтов и XObject по страницам, вложения, действия, формы, метаданные, шифрование и типы фильтров. Номера косвенных объектов не используют как единственный ключ: после пересохранения они часто меняются без изменения смысла. Содержимое потоков сравнивают после одинаковой цепочки декодирования и с отдельной фиксацией исходных байтов.

Контентные потоки можно нормализовать по операторам, но такая процедура должна понимать строки, массивы и inline images. Простое удаление пробелов или сортировка строк нарушает синтаксис. Для первого этапа достаточно сравнить SHA-256 декодированного потока, использованные ресурсы и число операторов. Различие затем исследуют адресно. Для страниц с формами и прозрачностью визуальное совпадение подтверждают рендерингом при одинаковом размере и цветовом профиле.

Отчёт разделяет ожидаемые и неожиданные изменения. Например, снятие шифрования закономерно меняет Encrypt и байты многих объектов, но не должно удалять вложение или менять MediaBox. Декомпрессия меняет Filter и Length, но декодированное содержимое должно сохраниться. Такая матрица критериев делает проверку воспроизводимой и помогает обнаружить побочный эффект, который не виден по размеру файла или успешному коду завершения.

Пароли, разрешения и границы доверия

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

При диагностике фиксируют алгоритм, длину ключа и доступные флаги разрешений, не записывая секрет. Пароль не передают в командной строке общего сервера, если его могут увидеть другие пользователи или журнал аудита. Для автоматизации применяют защищённый канал ввода или краткоживущий секрет в изолированном процессе, затем очищают временные файлы. Ошибку расшифрования не обходят подбором; её отделяют от повреждения xref и неподдерживаемого Security Handler.

После создания защищённой копии проверяют реальное поведение в целевых читателях. Один просмотрщик может игнорировать отдельное разрешение, другой — не поддерживать выбранный AES. В отчёте указывают проверенную операцию: открытие без пароля, печать, копирование текста или изменение. Общая фраза файл защищён слишком неточна. Незашифрованный оригинал хранится по тем же или более строгим правилам, чем пароль.

Регрессионный набор для Ruby-сценариев

Сценарий, который работает на одном PDF, ещё не готов к пакетной обработке. Набор тестов должен включать обычный xref, xref stream, object streams, инкрементальные обновления, зашифрованные документы, страницы с наследуемыми ресурсами, AcroForm, XFA, вложения, несколько видов изображений и файл с намеренно повреждённой ссылкой. Для каждого образца заранее определяют ожидаемый статус и контрольные свойства результата.

Тесты проверяют не только отсутствие исключения. Они перечитывают выход, сравнивают число страниц и ключевые объекты, запускают структурный валидатор и, где нужно, рендерят эталонные страницы. Отрицательные случаи должны завершаться контролируемым сообщением без частично записанного файла.

После обновления Ruby, gem-зависимостей или системного пакета весь набор запускают заново в чистой среде. В журнале сохраняют версии и контрольные суммы тестовых файлов. Если результат изменился, сначала сравнивают канонический структурный отчёт, затем декодированные потоки и рендеринг. Так отделяют регрессию от изменения xref или нумерации objects.

Проверка результата перед передачей

Перед выдачей файла убеждаются, что исходник не был перезаписан, а итог открывается повторным PDF.read и независимым валидатором. Проверяют число страниц, размеры листов, закладки, формы, вложения, метаданные, действия и шифрование согласно цели операции. Для визуально значимых изменений рендерят все затронутые страницы, а не только первую. Контрольная сумма результата входит в сопроводительный журнал.

Если извлекались ресурсы, к ним прилагают карту файл — косвенный объект — место ссылки — фактический тип. Если удалялось активное содержимое, повторный инвентаризатор должен показать нулевой или заранее допустимый список действий. Если выполнялась декомпрессия, сравнивают декодированные потоки. Если создавался минимальный тест, перечисляют сознательно удалённые части, чтобы его не приняли за полноценный документ.

Ограничения формулируют конкретно: не проверена криптографическая подпись, не подтверждено соответствие PDF/A, не выполнялся JavaScript, не рендерились определённые страницы. Успешный запуск Origami не расширяет область проверки автоматически. Такой отчёт позволяет следующему специалисту воспроизвести действия, понять границы вывода и не смешать структурную корректность с визуальной, криптографической или пригодностью для длительного хранения.

Итоговый рабочий подход

Origami PDF полезен, когда документ рассматривается как граф объектов. Готовые команды закрывают проверку политикой, метаданные, извлечение ресурсов, шифрование, декомпрессию и уменьшение проблемного файла. Ruby API позволяет перейти к точному обходу, созданию и изменению словарей, ссылок и потоков.

Начинают с копии и одной цели: найти действие, выгрузить вложения, сравнить структуры или воспроизвести ошибку. После каждого преобразования файл читают заново и проверяют независимым средством. Такой порядок уменьшает риск незаметно потерять данные или принять успешно записанный, но структурно спорный PDF за корректный.

Для визуальной правки нужен редактор, для строгой PDF/A-проверки — валидатор, для подписи — криптографическое средство, для динамического поведения — песочница. Origami занимает техническую часть процесса: показывает внутренние объекты и позволяет описать воспроизводимое преобразование кодом с контролем каждого шага.