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

Pro редакция

Webview

Webview отдаёт линеаризованный (Fast Web View) PDF по HTTP, чтобы клиент мог начать отрисовку страницы 1 из небольшого ведущего префикса, пока остаток файла ещё в пути. Он оборачивает «сырые» байты как LinearizedDocument, отвечает на запросы Range ответами частичного содержимого по RFC 9110 через PSR-7-объект ByteRangeResponder и может доказать (через FirstPageProber), что первая страница самодостаточна в префиксе.

Эта возможность поставляется в NextPDF Pro (nextpdf/pro) и активируется лицензионным конвертом уровня Pro. Развёртывание без соответствующего права не загружает классы этой возможности. Сравнить редакции и получить лицензию.

Отдельного пофункционального лицензионного флага нет. Объект-ответчик подключается к вашим собственным фабрикам PSR-17 во время выполнения — ResponseFactoryInterface и StreamFactoryInterface, — а медиатип по умолчанию равен application/pdf как аргумент конструктора, а не лицензионный переключатель.

Окно терминала
composer require nextpdf/pro

Код находится в пространстве имён NextPDF\Pro\Webview.

Линеаризованный PDF размечен так, что первая страница документа — словарь параметров линеаризации, первичный поток подсказок и объекты страницы 1 — располагается в ведущей секции, заканчивающейся на смещении /E. Webview превращает эту разметку в прогрессивную отдачу.

LinearizedDocument::fromBytes() разбирает байты через LinearizationView из стороны чтения Core (Pro никогда не реализует разбор линеаризации заново) и отклоняет всё, что не является пригодным линеаризованным документом: вовсе не линеаризованное, объявленную длину /L, не совпадающую с реальной длиной в байтах, либо смещение конца первой страницы /E, которое не является положительным смещением внутри файла. Поэтому конструирование тотально — раз уж вы держите LinearizedDocument, каждое выставляемое им смещение заслуживает доверия.

ByteRangeResponder затем отвечает на HTTP-запрос. Он реализован только против PSR-7 / PSR-17, без привязки к фреймворку. Он всегда объявляет Accept-Ranges: bytes и сильный, детерминированный SHA-256-ETag, разбирает заголовок Range клиента по RFC 9110 §14 и возвращает либо полный 200 OK, либо 206 Partial Content с одним диапазоном, либо 206-ответ multipart/byteranges для нескольких диапазонов, либо 416 Range Not Satisfiable.

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

Webview никогда не разбирает линеаризацию заново. Он заимствует LinearizationView из стороны чтения Core, поэтому слой отдачи наследует один проверенный парсер, а не вторую, расходящуюся копию. Конструирование намеренно тотально. LinearizedDocument::fromBytes() отклоняет некорректную разметку заранее, поэтому каждое смещение, которому доверяет ответ на Range, было проверено первым. Объект-ответчик говорит только на PSR-7 и PSR-17, поэтому один и тот же код отдаёт линеаризованный PDF из любого HTTP-стека. Именно эта дисциплина делает прогрессивную отдачу по диапазонам безопасной для предоставления недоверенным клиентам в больших объёмах.

Проектный контекст: Генерация документов в больших объёмах.

Как работает прогрессивная отдача по диапазонам байтов

Заголовок раздела «Как работает прогрессивная отдача по диапазонам байтов»
  1. Постройте LinearizedDocument из отрисованных байтов PDF. Невалидный вход сразу же вызывает UnsupportedDocumentException.
  2. Передайте документ и входящий PSR-7-объект ServerRequestInterface в ByteRangeResponder::respond(). Объект-ответчик читает Range (и необязательное предусловие If-Range) и формирует корректный PSR-7-объект ResponseInterface.
  3. Клиент сначала запрашивает ведущий префикс (или вы проталкиваете его через firstPageResponse()), отрисовывает страницу 1, затем запрашивает остальные диапазоны по мере прокрутки пользователем.

Модель диапазонов байтов использует включающие смещения по RFC 9110 §14.1.2: ByteRange — это firstBytelastByte над представлением длины contentLength, а его поле Content-Range равно bytes first-last/length.

  • LinearizedDocument::fromBytes() тотален: нелинеаризованный документ, несовпадение /L либо неположительное / выходящее за пределы файла смещение /E — каждое из этого вызывает UnsupportedDocumentException вместо создания небезопасного документа.
  • ETag — это сильный SHA-256-тег сущности над точными байтами, мемоизированный один раз при конструировании. Идентичный вход отрисовки даёт идентичные байты и, следовательно, идентичный ETag, поэтому кэши и If-Range ведут себя предсказуемо.
  • Запрос без применимого Range возвращает 200 OK с полным телом. If-Range, не совпадающий с текущим сильным ETag, приводит к тому, что Range игнорируется и возвращается полный 200 (RFC 9110 §13.1.5). Учитывается только форма If-Range с сильным тегом сущности; If-Range в виде HTTP-даты трактуется как несовпадение.
  • Нераспознанная единица диапазона или синтаксически невалидный Range игнорируется, и возвращается полный 200 (RFC 9110 §14.2).
  • Один удовлетворимый диапазон возвращает 206 Partial Content с Content-Range; несколько удовлетворимых диапазонов возвращают 206 multipart/byteranges. Валидные диапазоны байтов, среди которых ни один не удовлетворим, возвращают 416 с Content-Range: bytes */length (RFC 9110 §15.3.7).
  • firstPageResponse() выдаёт 206, несущий ровно диапазон байтов первой страницы [0, /E - 1] — это серверная push-форма «первая страница до полного скачивания».

Следующее отражает документированный публичный API. Репозиторий не поставляет исполняемый пример для этого модуля.

use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
$document = LinearizedDocument::fromBytes($pdfBytes);
$responder = new ByteRangeResponder($responseFactory, $streamFactory);
$response = $responder->respond($document, $request);

Пример кода — проталкивание и проверка первой страницы

Заголовок раздела «Пример кода — проталкивание и проверка первой страницы»
use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
use NextPDF\Pro\Webview\FirstPageProber;
use NextPDF\Pro\Webview\Exception\UnsupportedDocumentException;
try {
$document = LinearizedDocument::fromBytes($pdfBytes);
} catch (UnsupportedDocumentException $e) {
// Not a usable linearized document — fall back to plain full delivery.
// ...
return;
}
$prober = new FirstPageProber($document);
if ($prober->isFirstPageSelfContained()) {
// Push exactly the first page's bytes for an instant render.
$response = (new ByteRangeResponder($responseFactory, $streamFactory))
->firstPageResponse($document);
}
  • Webview требует по-настоящему линеаризованного PDF. Если отрисованный документ не линеаризован, включите линеаризацию во время отрисовки либо отдавайте его обычной полной отдачей — respondToBytes() всё же может отдавать диапазоны над произвольными (нелинеаризованными) байтами, когда вам нужна лишь поддержка диапазонов, а не семантика первой страницы.
  • Инкрементальные обновления важны: документ, к которому дописали данные сверх объявленного /L, отклоняется как несовпадение длины, потому что смещения по диапазонам байтов больше не заслуживали бы доверия.
  • Объект-ответчик ограничивает число различных диапазонов, которые он учитывает на один запрос. Запрос, просящий больше объединённых диапазонов, чем предел, либо больше суммарных байтов, чем всё представление, получает игнорирование своего Range и отдачу полного 200.

Префикс первой страницы — это смещение конца первой страницы /E, ограниченное длиной файла, поэтому FirstPageProber::prefixFraction() сообщает, насколько мала начальная выборка относительно всего файла — для многостраничного документа в этом и состоит весь смысл Fast Web View. Построение ответа нарезает строку байтов в памяти; стоимость пропорциональна выбранным байтам. ETag вычисляется один раз на документ. Измеряйте на репрезентативных документах.

Считайте вход недоверенным. LinearizedDocument::fromBytes() проверяет инварианты линеаризации до того, как будет использовано хоть одно смещение. Объект-ответчик отклоняет contentType, содержащий управляющие символы, чтобы предотвратить внедрение в заголовки, выводит multipart-границу, которая гарантированно не встречается внутри тела, и объединяет перекрывающиеся диапазоны, ограничивая их число и суммарный размер для защиты от класса отказа в обслуживании через усиление multipart-диапазонов (Apache HTTPD CVE-2011-3192). Этот модуль не журналирует содержимое документа.

Отдача по диапазонам байтов следует RFC 9110 (HTTP Semantics) — §14 для запросов диапазонов, §13.1.5 для If-Range и §15.3.7 для 416. Модель линеаризованного документа — это разметка Fast Web View, описанная в ISO 32000-2 Annex F. Модуль не утверждает никаких иных внешних идентификаторов пунктов сверх поведения, проверенного его тестами.

Enterprise не меняет поведение Webview. Enterprise добавляет возможности соответствия требованиям и архивирования более высокого уровня, документированные отдельно; они не требуются для отдачи линеаризованного PDF по диапазонам байтов.

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