Pro edition
Webview
At a glance
Section titled “At a glance”Webview delivers a linearized (Fast Web View) PDF over HTTP so a client can start rendering page 1 from a small leading prefix while the rest of the file is still in flight. It wraps the raw bytes as a LinearizedDocument, answers Range requests with RFC 9110 partial-content responses through a PSR-7 ByteRangeResponder, and can prove (via FirstPageProber) that the first page is self-contained in the prefix.
Availability & licensing
Section titled “Availability & licensing”This capability ships in NextPDF Pro (nextpdf/pro) and activates with a Pro-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.
There is no separate per-feature license flag. The responder is wired to your own PSR-17 factories at runtime — a ResponseFactoryInterface and a StreamFactoryInterface — and the media type defaults to application/pdf as a constructor argument, not a license switch.
Install
Section titled “Install”composer require nextpdf/proThe code lives under the NextPDF\Pro\Webview namespace.
Conceptual overview
Section titled “Conceptual overview”A linearized PDF is laid out so the document’s first page — the linearization parameter dictionary, the primary hint stream, and page 1’s objects — sits in a leading section that ends at the /E offset. Webview turns that layout into progressive delivery.
LinearizedDocument::fromBytes() parses the bytes through Core’s read-side LinearizationView (Pro never re-implements linearization parsing) and rejects anything that is not a usable linearized document: not linearized at all, a declared /L length that does not match the real byte length, or a /E end-of-first-page offset that is not a positive offset inside the file. Construction is therefore total — once you hold a LinearizedDocument, every offset it exposes is trustworthy.
ByteRangeResponder then answers an HTTP request. It is implemented against PSR-7 / PSR-17 only, with no framework coupling. It always advertises Accept-Ranges: bytes and a strong, deterministic SHA-256 ETag, parses the client’s Range header per RFC 9110 §14, and returns either a full 200 OK, a 206 Partial Content single range, a 206 multipart/byteranges response for several ranges, or a 416 Range Not Satisfiable.
FirstPageProber is the structural proof side: it quantifies the first-page prefix, the fraction of the whole file that prefix represents, and whether the primary hint stream lies fully inside it — the property that lets a reader locate page-1 objects from the prefix alone.
Why it works this way
Section titled “Why it works this way”Webview never re-parses linearization itself. It borrows Core’s read-side LinearizationView, so the delivery layer inherits one audited parser rather than a second, drifting copy. Construction is deliberately total. LinearizedDocument::fromBytes() rejects a malformed layout up front, so every offset a Range response trusts was validated first. The responder speaks only PSR-7 and PSR-17, so the same code serves a linearized PDF from any HTTP stack. That discipline is what makes progressive range delivery safe to expose to untrusted clients at volume.
Design background: High-volume document generation.
How progressive byte-range serving works
Section titled “How progressive byte-range serving works”- Build a
LinearizedDocumentfrom the rendered PDF bytes. Invalid input raisesUnsupportedDocumentExceptionup front. - Hand the document and the incoming PSR-7
ServerRequestInterfacetoByteRangeResponder::respond(). The responder readsRange(and the optionalIf-Rangeprecondition), and produces the correct PSR-7ResponseInterface. - The client requests the leading prefix first (or you push it with
firstPageResponse()), renders page 1, then requests the remaining ranges as the user scrolls.
The byte-range model uses inclusive offsets per RFC 9110 §14.1.2: a ByteRange is firstByte–lastByte over a representation of contentLength, and its Content-Range field is bytes first-last/length.
Behavior contract
Section titled “Behavior contract”LinearizedDocument::fromBytes()is total: a non-linearized document, a/Lmismatch, or a non-positive / out-of-file/Eoffset each raiseUnsupportedDocumentExceptioninstead of producing an unsafe document.- The
ETagis a strong SHA-256 entity-tag over the exact bytes, memoised once at construction. Identical render input yields identical bytes and therefore an identicalETag, so caches andIf-Rangebehave predictably. - A request with no applicable
Rangereturns200 OKwith the full body. AnIf-Rangethat does not match the current strongETagcauses theRangeto be ignored and a full200to be returned (RFC 9110 §13.1.5). Only the strong entity-tag form ofIf-Rangeis honoured; an HTTP-dateIf-Rangeis treated as a non-match. - An unrecognised range unit or a syntactically invalid
Rangeis ignored and a full200is returned (RFC 9110 §14.2). - One satisfiable range returns
206 Partial ContentwithContent-Range; multiple satisfiable ranges return206multipart/byteranges. Valid byte ranges with none satisfiable return416withContent-Range: bytes */length(RFC 9110 §15.3.7). firstPageResponse()emits a206carrying exactly the first page’s byte range[0, /E - 1]— the server-push form of “first page before full download”.
Code sample — Quick start
Section titled “Code sample — Quick start”The following reflects the documented public API. The repository does not ship a runnable example for this module.
use NextPDF\Pro\Webview\LinearizedDocument;use NextPDF\Pro\Webview\ByteRangeResponder;
$document = LinearizedDocument::fromBytes($pdfBytes);$responder = new ByteRangeResponder($responseFactory, $streamFactory);
$response = $responder->respond($document, $request);Code sample — First-page push and probing
Section titled “Code sample — First-page push and probing”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);}Edge cases & gotchas
Section titled “Edge cases & gotchas”- Webview requires a genuinely linearized PDF. If the rendered document is not linearized, enable linearization at render time, or serve it with plain full delivery —
respondToBytes()can still serve ranges over arbitrary (non-linearized) bytes when you only need range support, not first-page semantics. - Incremental updates matter: a document that was appended to beyond its declared
/Lis rejected as a length mismatch, because byte-range offsets would no longer be trustworthy. - The responder caps the number of distinct ranges it honours per request. A request asking for more coalesced ranges than the cap, or for more total bytes than the whole representation, has its
Rangeignored and is served a full200.
Performance
Section titled “Performance”The first-page prefix is the /E end-of-first-page offset clamped to the file length, so FirstPageProber::prefixFraction() reports how small the initial fetch is relative to the whole file — for a many-page document this is the entire point of Fast Web View. Response building slices the in-memory byte string; cost is proportional to the bytes selected. The ETag is computed once per document. Measure with representative documents.
Security notes
Section titled “Security notes”Treat input as untrusted. LinearizedDocument::fromBytes() validates linearization invariants before any offset is used. The responder rejects a contentType containing control characters to prevent header injection, derives a multipart boundary that is guaranteed not to occur inside the body, and coalesces overlapping ranges and bounds their count and total size to defend against the multipart range-amplification class of denial-of-service (Apache HTTPD CVE-2011-3192). This module logs no document content.
Conformance
Section titled “Conformance”Byte-range delivery follows RFC 9110 (HTTP Semantics) — §14 for range requests, §13.1.5 for If-Range, and §15.3.7 for 416. The linearized-document model is the Fast Web View layout described by ISO 32000-2 Annex F. The module asserts no further external clause identifiers beyond behavior verified by its tests.
Enterprise boundary note
Section titled “Enterprise boundary note”Enterprise does not change Webview behavior. Enterprise adds higher-tier compliance and archival features documented separately; they are not required to serve a linearized PDF over byte ranges.
Publication boundary
Section titled “Publication boundary”This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.