Pular para o conteúdo
getnextpdf.com

Pro edição

Webview

O Webview entrega um PDF linearizado (Fast Web View) por HTTP para que um cliente possa começar a renderizar a página 1 a partir de um pequeno prefixo inicial enquanto o restante do arquivo ainda está em trânsito. Ele encapsula os bytes brutos como um LinearizedDocument, responde a requisições Range com respostas de conteúdo parcial do RFC 9110 por meio de um ByteRangeResponder PSR-7, e pode provar (via FirstPageProber) que a primeira página é autocontida no prefixo.

Esse recurso vem no NextPDF Pro (nextpdf/pro) e é ativado com um envelope de licença de nível Pro. Uma implantação sem essa habilitação não carrega as classes do recurso. Compare as edições e obtenha uma licença.

Não há um sinalizador de licença separado por recurso. O responder é conectado às suas próprias fábricas PSR-17 em runtime — uma ResponseFactoryInterface e uma StreamFactoryInterface — e o media type tem como padrão application/pdf como um argumento do construtor, não como uma chave de licença.

Terminal window
composer require nextpdf/pro

O código está sob o namespace NextPDF\Pro\Webview.

Um PDF linearizado é disposto de modo que a primeira página do documento — o dicionário de parâmetros de linearização, o stream de hint primário e os objetos da página 1 — fique em uma seção inicial que termina no offset /E. O Webview transforma esse layout em entrega progressiva.

LinearizedDocument::fromBytes() analisa os bytes por meio da LinearizationView do lado de leitura do Core (o Pro nunca reimplementa a análise de linearização) e rejeita qualquer coisa que não seja um documento linearizado utilizável: não linearizado de forma alguma, um comprimento /L declarado que não corresponde ao comprimento real em bytes, ou um offset /E de fim da primeira página que não seja um offset positivo dentro do arquivo. A construção é, portanto, total — uma vez que você detenha um LinearizedDocument, cada offset que ele expõe é confiável.

ByteRangeResponder, então, responde a uma requisição HTTP. Ele é implementado apenas contra PSR-7 / PSR-17, sem acoplamento a framework. Ele sempre anuncia Accept-Ranges: bytes e um ETag SHA-256 forte e determinístico, analisa o cabeçalho Range do cliente conforme o RFC 9110 §14, e retorna um 200 OK completo, um único intervalo 206 Partial Content, uma resposta 206 multipart/byteranges para vários intervalos, ou um 416 Range Not Satisfiable.

FirstPageProber é o lado da prova estrutural: ele quantifica o prefixo da primeira página, a fração de todo o arquivo que esse prefixo representa e se o stream de hint primário está totalmente dentro dele — a propriedade que permite a um leitor localizar os objetos da página 1 apenas a partir do prefixo.

O Webview nunca reanalisa a linearização por conta própria. Ele toma emprestada a LinearizationView do lado de leitura do Core, de modo que a camada de entrega herda um único parser auditado em vez de uma segunda cópia que pode divergir. A construção é deliberadamente total. LinearizedDocument::fromBytes() rejeita um layout malformado de imediato, portanto cada offset em que uma resposta Range confia foi validado primeiro. O responder fala apenas PSR-7 e PSR-17, então o mesmo código serve um PDF linearizado a partir de qualquer stack HTTP. É essa disciplina que torna a entrega progressiva por intervalo segura para expor a clientes não confiáveis em volume.

Contexto de design: Geração de documentos em alto volume.

Como funciona a entrega progressiva por intervalo de bytes

Seção intitulada “Como funciona a entrega progressiva por intervalo de bytes”
  1. Construa um LinearizedDocument a partir dos bytes do PDF renderizado. Uma entrada inválida levanta UnsupportedDocumentException de imediato.
  2. Entregue o documento e o ServerRequestInterface PSR-7 recebido ao ByteRangeResponder::respond(). O responder lê Range (e a pré-condição opcional If-Range) e produz o ResponseInterface PSR-7 correto.
  3. O cliente requisita primeiro o prefixo inicial (ou você o envia via push com firstPageResponse()), renderiza a página 1 e, em seguida, requisita os intervalos restantes conforme o usuário rola a página.

O modelo de intervalo de bytes usa offsets inclusivos conforme o RFC 9110 §14.1.2: um ByteRange é firstBytelastByte sobre uma representação de contentLength, e seu campo Content-Range é bytes first-last/length.

  • LinearizedDocument::fromBytes() é total: um documento não linearizado, uma divergência de /L, ou um offset /E não positivo / fora do arquivo levantam, cada um, UnsupportedDocumentException em vez de produzir um documento inseguro.
  • O ETag é uma entity-tag SHA-256 forte sobre os bytes exatos, memoizada uma única vez na construção. Uma entrada de renderização idêntica produz bytes idênticos e, portanto, um ETag idêntico, de modo que os caches e o If-Range se comportem de forma previsível.
  • Uma requisição sem Range aplicável retorna 200 OK com o corpo completo. Um If-Range que não corresponde ao ETag forte atual faz com que o Range seja ignorado e um 200 completo seja retornado (RFC 9110 §13.1.5). Apenas a forma de entity-tag forte de If-Range é honrada; um If-Range de data HTTP é tratado como não correspondente.
  • Uma unidade de intervalo não reconhecida ou um Range sintaticamente inválido é ignorado e um 200 completo é retornado (RFC 9110 §14.2).
  • Um intervalo satisfazível retorna 206 Partial Content com Content-Range; vários intervalos satisfazíveis retornam 206 multipart/byteranges. Intervalos de bytes válidos sem nenhum satisfazível retornam 416 com Content-Range: bytes */length (RFC 9110 §15.3.7).
  • firstPageResponse() emite um 206 carregando exatamente o intervalo de bytes da primeira página [0, /E - 1] — a forma de server-push de “primeira página antes do download completo”.

O exemplo a seguir reflete a API pública documentada. O repositório não fornece um exemplo executável para este módulo.

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

Exemplo de código — Push da primeira página e sondagem

Seção intitulada “Exemplo de código — Push da primeira página e sondagem”
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);
}
  • O Webview requer um PDF genuinamente linearizado. Se o documento renderizado não for linearizado, habilite a linearização no momento da renderização, ou sirva-o com entrega completa simples — respondToBytes() ainda pode servir intervalos sobre bytes arbitrários (não linearizados) quando você precisa apenas de suporte a intervalos, e não da semântica de primeira página.
  • As atualizações incrementais importam: um documento que recebeu anexos além do seu /L declarado é rejeitado como uma divergência de comprimento, porque os offsets de intervalo de bytes deixariam de ser confiáveis.
  • O responder limita o número de intervalos distintos que honra por requisição. Uma requisição que pede mais intervalos coalescidos do que o limite, ou mais bytes totais do que toda a representação, tem seu Range ignorado e recebe um 200 completo.

O prefixo da primeira página é o offset /E de fim da primeira página fixado ao comprimento do arquivo, de modo que FirstPageProber::prefixFraction() informa quão pequeno é o fetch inicial em relação a todo o arquivo — para um documento de muitas páginas, esse é todo o propósito do Fast Web View. A construção da resposta fatia a string de bytes em memória; o custo é proporcional aos bytes selecionados. O ETag é calculado uma vez por documento. Meça com documentos representativos.

Trate a entrada como não confiável. LinearizedDocument::fromBytes() valida os invariantes de linearização antes que qualquer offset seja usado. O responder rejeita um contentType que contenha caracteres de controle para evitar injeção de cabeçalho, deriva um boundary de multipart com garantia de não ocorrer dentro do corpo, e coalesce intervalos sobrepostos e limita sua quantidade e tamanho total para se defender contra a classe de negação de serviço por amplificação de intervalo multipart (Apache HTTPD CVE-2011-3192). Este módulo não registra nenhum conteúdo de documento.

A entrega por intervalo de bytes segue o RFC 9110 (HTTP Semantics) — §14 para requisições de intervalo, §13.1.5 para If-Range e §15.3.7 para 416. O modelo de documento linearizado é o layout Fast Web View descrito no ISO 32000-2 Annex F. O módulo não afirma nenhum identificador de cláusula externo além do comportamento verificado por seus testes.

O Enterprise não altera o comportamento do Webview. O Enterprise acrescenta recursos de conformidade e arquivamento de nível superior, documentados separadamente; eles não são necessários para servir um PDF linearizado por intervalos de bytes.

Esta página documenta apenas o comportamento externamente observável e a superfície pública de API suportada. Caminhos de namespace internos, classes auxiliares, tabelas de mecanismos, nomes de arquivos de runbook e prefixos de ticket estão fora de escopo.