Перейти к содержимому

Разбор PDF и проверка структурных сведений

В этом рецепте резервный режим Quick в инспекторе Core читает структурные сведения из файла Portable Document Format (PDF). В результате вы получаете версию, число страниц, флаги шифрования, подписи и вложений, размер файла и флаги риска. Режим Quick работает полностью внутри процесса: без сопроцесса Spectrum и без доступа к сети. Используйте его для быстрой сортировки, а не как валидатор.

Окно терминала
composer require nextpdf/core:^3

В PDF-файле версия указана в заголовке файла (ISO 32000-2 §7.5.2). Трейлер содержит идентификатор файла (/ID) в виде двух байтовых строк (ISO 32000-2 §7.5.5). При наличии подписи словарь подписи хранит структуру Cryptographic Message Syntax (CMS) SignedData в кодировке Distinguished Encoding Rules (DER) в Contents (ISO 32000-2 §12.8.1). Резервный режим Quick выполняет ограниченное сканирование байтов документа, чтобы определить версию, оценить число страниц и выставить флаги наличия шифрования, подписи и вложений.

Создайте new Inspector(), затем вызовите ->inspect(string $pdfData, InspectConfig::quick()). Метод возвращает InspectResult с полями $pdfVersion, $pageCount, $isEncrypted, $hasSigned, $hasAttachments, $fileSizeBytes, $riskFlags и вспомогательным методом hasRisks().

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Inspect\InspectConfig;
use NextPDF\Inspect\Inspector;
$pdf = file_get_contents(__DIR__ . '/document.pdf');
$result = (new Inspector())->inspect($pdf, InspectConfig::quick());
printf(
"v%s, %d page(s), encrypted=%s, signed=%s\n",
$result->pdfVersion ?? '?',
$result->pageCount,
$result->isEncrypted ? 'yes' : 'no',
$result->hasSigned ? 'yes' : 'no',
);

Эта самодостаточная программа выполняется в среде запуска рецептов. Она повторяет examples/39-parse-and-inspect-pdf.php: строит небольшой многостраничный PDF в памяти, считывает его структурные сведения в резервном режиме Quick и маршрутизирует документ по этим сведениям, но никогда не по вердикту доверия. Ветвь маршрутизации приведена для иллюстрации. Замените её собственным конвейером, очередью верификации и карантином.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
use NextPDF\Inspect\InspectConfig;
use NextPDF\Inspect\Inspector;
// A self-contained input so the program runs with no external file.
$doc = Document::createStandalone();
$doc->setTitle('Parse-and-inspect demo');
$doc->setAuthor('NextPDF Cookbook');
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 10, 'Page one of the parse-and-inspect demonstration.', newLine: true);
$doc->addPage();
$doc->cell(0, 10, 'Page two.', newLine: true);
$pdf = $doc->getPdfData();
$result = (new Inspector())->inspect($pdf, InspectConfig::quick());
echo 'PDF version : ' . ($result->pdfVersion ?? 'unknown') . "\n";
echo 'Pages : ' . $result->pageCount . "\n";
echo 'Encrypted : ' . ($result->isEncrypted ? 'yes' : 'no') . "\n";
echo 'Signed : ' . ($result->hasSigned ? 'yes' : 'no') . "\n";
echo 'Attachments : ' . ($result->hasAttachments ? 'yes' : 'no') . "\n";
echo 'File size : ' . $result->fileSizeBytes . " bytes\n";
echo 'Risk flags : ' . ($result->hasRisks() ? count($result->riskFlags) : 0) . "\n";
// Route on structural facts, not trust verdicts. Replace these calls with
// your own pipeline / verifier queue / quarantine.
if ($result->isEncrypted) {
// $pipeline->decryptThenContinue($pdf);
echo "Route: decrypt-then-continue\n";
} elseif ($result->hasSigned) {
// $verifierQueue->enqueue($pdf); // see the signature-inspect recipe
echo "Route: enqueue for cryptographic verification\n";
} elseif ($result->hasRisks()) {
// $quarantine->hold($pdf, $result->riskFlags);
echo "Route: quarantine (risk flags present)\n";
} else {
// $pipeline->continue($pdf);
echo "Route: continue (no risks, unsigned, unencrypted)\n";
}
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script under the
// semantic profile; emit the document to the side-channel.
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
file_put_contents($out !== false && $out !== '' ? $out : __DIR__ . '/inspected.pdf', $pdf);

Ожидаемый стандартный вывод (STDOUT): версия и размер зависят от сборки; демонстрационный PDF не зашифрован, не подписан и не содержит флагов риска:

PDF version : <version>
Pages : 2
Encrypted : no
Signed : no
Attachments : no
File size : <n> bytes
Risk flags : 0
Route: continue (no risks, unsigned, unencrypted)
  • Quick — это сортировка, а не проверка. Он сообщает, что присутствует, а что отсутствует. Он не проверяет подписи, не расшифровывает содержимое и не подтверждает соответствие. Рассматривайте результат как входные данные для маршрутизации.
  • Число страниц — это оценка. Резервный режим Quick подсчитывает маркеры объектов-страниц. Намеренно искажённый граф объектов может дать неверный подсчёт. Когда нужен точный подсчёт, используйте уровни глубины на основе Spectrum.
  • Уровням Standard/Full нужен сопроцесс. new InspectConfig() (глубина Standard) и InspectConfig::full() требуют сопроцесса Spectrum. Когда он недоступен, они выбрасывают INSPECT-SIDECAR-001 и не переключаются молча на Quick.
  • Пустые входные данные. При передаче пустой строки выбрасывается исключение проверки с сообщением “PDF data must not be empty”.
  • Область действия флага шифрования. Флаг отражает наличие записи /Encrypt в трейлере. Инспектор не расшифровывает файл, помеченный этим флагом.

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

Инспектор выполняется внутри процесса и читает только структурные маркеры. Ни один байт документа не покидает хост, и текст документа не извлекается. Флаг риска, например наличие встроенного JavaScript, — это рекомендательный сигнал для маршрутизации. Это не утверждение о том, что файл безопасен или небезопасен.

УтверждениеСпецификацияПунктreference_id (идентификатор ссылки)
В заголовке файла записана версия PDF.ISO 32000-2§7.5.2
Запись трейлера /ID — это идентификатор файла из двух байтовых строк.ISO 32000-2§7.5.5
Запись Contents в словаре подписи содержит структуру SignedData в формате DER CMS.ISO 32000-2§12.8.1

Этот рецепт сообщает только структурные сведения. Он не утверждает, что файл корректен, безопасен или соответствует требованиям.

Уровни глубины проверки Standard и Full выполняются через сопроцесс Spectrum. Они добавляют более детальный анализ объектов, шрифтов и изображений. Описанный здесь резервный режим Quick относится к Core и работает офлайн.