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-стека. Именно эта дисциплина делает прогрессивную отдачу по диапазонам безопасной для предоставления недоверенным клиентам в больших объёмах.
Проектный контекст: Генерация документов в больших объёмах.
Как работает прогрессивная отдача по диапазонам байтов
Заголовок раздела «Как работает прогрессивная отдача по диапазонам байтов»- Постройте
LinearizedDocumentиз отрисованных байтов PDF. Невалидный вход сразу же вызываетUnsupportedDocumentException. - Передайте документ и входящий PSR-7-объект
ServerRequestInterfaceвByteRangeResponder::respond(). Объект-ответчик читаетRange(и необязательное предусловиеIf-Range) и формирует корректный PSR-7-объектResponseInterface. - Клиент сначала запрашивает ведущий префикс (или вы проталкиваете его через
firstPageResponse()), отрисовывает страницу 1, затем запрашивает остальные диапазоны по мере прокрутки пользователем.
Модель диапазонов байтов использует включающие смещения по RFC 9110 §14.1.2: ByteRange — это firstByte–lastByte над представлением длины 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; несколько удовлетворимых диапазонов возвращают206multipart/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
Заголовок раздела «Замечание о границе с Enterprise»Enterprise не меняет поведение Webview. Enterprise добавляет возможности соответствия требованиям и архивирования более высокого уровня, документированные отдельно; они не требуются для отдачи линеаризованного PDF по диапазонам байтов.
Граница публикации
Заголовок раздела «Граница публикации»Эта страница документирует только внешне наблюдаемое поведение и поддерживаемую поверхность публичного API. Внутренние пути пространств имён, вспомогательные классы, таблицы механизмов, имена файлов runbook и префиксы тикетов — вне области рассмотрения.