Aller au contenu
getnextpdf.com

Pro édition

Webview

Webview livre un PDF linéarisé (Fast Web View) sur HTTP, de sorte qu’un client puisse commencer à afficher la page 1 à partir d’un petit préfixe de tête pendant que le reste du fichier est encore en transit. Il enveloppe les octets bruts dans un LinearizedDocument, répond aux requêtes Range par des réponses à contenu partiel RFC 9110 via un ByteRangeResponder PSR-7, et peut prouver (via FirstPageProber) que la première page est autonome dans le préfixe.

Cette capacité est livrée dans NextPDF Pro (nextpdf/pro) et s’active avec une enveloppe de licence de palier Pro. Un déploiement sans ce droit ne charge pas les classes de la capacité. Compare les éditions et obtiens une licence.

Il n’y a pas d’indicateur de licence par fonctionnalité distinct. Le responder est câblé à tes propres fabriques PSR-17 au runtime — une ResponseFactoryInterface et une StreamFactoryInterface — et le type de média vaut par défaut application/pdf en tant qu’argument de constructeur, pas une bascule de licence.

Fenêtre de terminal
composer require nextpdf/pro

Le code se trouve sous l’espace de noms NextPDF\Pro\Webview.

Un PDF linéarisé est agencé de sorte que la première page du document — le dictionnaire de paramètres de linéarisation, le flux d’indices primaire et les objets de la page 1 — se trouve dans une section de tête qui se termine à l’offset /E. Webview transforme cet agencement en livraison progressive.

LinearizedDocument::fromBytes() analyse les octets via la LinearizationView côté lecture du Core (Pro ne réimplémente jamais l’analyse de linéarisation) et rejette tout ce qui n’est pas un document linéarisé exploitable : pas linéarisé du tout, une longueur /L déclarée qui ne correspond pas à la longueur réelle en octets, ou un offset de fin de première page /E qui n’est pas un offset positif à l’intérieur du fichier. La construction est donc totale — une fois que tu détiens un LinearizedDocument, chaque offset qu’il expose est digne de confiance.

ByteRangeResponder répond ensuite à une requête HTTP. Il est implémenté uniquement contre PSR-7 / PSR-17, sans couplage à un framework. Il annonce toujours Accept-Ranges: bytes et un ETag SHA-256 fort et déterministe, analyse l’en-tête Range du client conformément à RFC 9110 §14, et renvoie soit un 200 OK complet, soit une 206 Partial Content à plage unique, soit une réponse 206 multipart/byteranges pour plusieurs plages, soit un 416 Range Not Satisfiable.

FirstPageProber est le volet de preuve structurelle : il quantifie le préfixe de première page, la fraction du fichier entier que ce préfixe représente, et si le flux d’indices primaire se trouve entièrement à l’intérieur — la propriété qui permet à un lecteur de localiser les objets de la page 1 à partir du seul préfixe.

Webview ne réanalyse jamais la linéarisation lui-même. Il emprunte la LinearizationView côté lecture du Core, de sorte que la couche de livraison hérite d’un seul analyseur audité plutôt que d’une seconde copie qui dérive. La construction est délibérément totale. LinearizedDocument::fromBytes() rejette un agencement malformé d’emblée, de sorte que chaque offset auquel une réponse Range fait confiance a d’abord été validé. Le responder ne parle que PSR-7 et PSR-17, de sorte que le même code sert un PDF linéarisé depuis n’importe quelle pile HTTP. C’est cette discipline qui rend la livraison progressive par plage sûre à exposer à des clients non fiables à grande échelle.

Contexte de conception : Génération de documents à haut volume.

Comment fonctionne le service progressif par plage d’octets

Section intitulée « Comment fonctionne le service progressif par plage d’octets »
  1. Construis un LinearizedDocument à partir des octets du PDF rendu. Une entrée invalide lève UnsupportedDocumentException d’emblée.
  2. Passe le document et la ServerRequestInterface PSR-7 entrante à ByteRangeResponder::respond(). Le responder lit Range (et la précondition optionnelle If-Range) et produit la ResponseInterface PSR-7 correcte.
  3. Le client demande d’abord le préfixe de tête (ou tu le pousses avec firstPageResponse()), affiche la page 1, puis demande les plages restantes au fil du défilement de l’utilisateur.

Le modèle de plage d’octets utilise des offsets inclusifs conformément à RFC 9110 §14.1.2 : un ByteRange est firstBytelastByte sur une représentation de contentLength, et son champ Content-Range est bytes first-last/length.

  • LinearizedDocument::fromBytes() est total : un document non linéarisé, une incohérence /L, ou un offset /E non positif / hors fichier lèvent chacun UnsupportedDocumentException au lieu de produire un document non sûr.
  • L’ETag est une étiquette d’entité SHA-256 forte sur les octets exacts, mémoïsée une fois à la construction. Une même entrée de rendu produit des octets identiques et donc un ETag identique, de sorte que les caches et If-Range se comportent de façon prévisible.
  • Une requête sans Range applicable renvoie 200 OK avec le corps complet. Un If-Range qui ne correspond pas à l’ETag fort courant fait ignorer le Range et renvoyer un 200 complet (RFC 9110 §13.1.5). Seule la forme d’étiquette d’entité forte de If-Range est honorée ; un If-Range à date HTTP est traité comme une non-correspondance.
  • Une unité de plage non reconnue ou un Range syntaxiquement invalide est ignoré et un 200 complet est renvoyé (RFC 9110 §14.2).
  • Une plage satisfaisable renvoie 206 Partial Content avec Content-Range ; plusieurs plages satisfaisables renvoient 206 multipart/byteranges. Des plages d’octets valides dont aucune n’est satisfaisable renvoient 416 avec Content-Range: bytes */length (RFC 9110 §15.3.7).
  • firstPageResponse() émet un 206 portant exactement la plage d’octets de la première page [0, /E - 1] — la forme « première page avant téléchargement complet » par poussée serveur.

Ce qui suit reflète l’API publique documentée. Le dépôt ne livre pas d’exemple exécutable pour ce 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);

Exemple de code — Poussée et sondage de la première page

Section intitulée « Exemple de code — Poussée et sondage de la première page »
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 nécessite un PDF véritablement linéarisé. Si le document rendu n’est pas linéarisé, active la linéarisation au moment du rendu, ou sers-le par livraison complète simple — respondToBytes() peut tout de même servir des plages sur des octets arbitraires (non linéarisés) lorsque tu n’as besoin que de la prise en charge des plages, pas de la sémantique de première page.
  • Les mises à jour incrémentales comptent : un document auquel on a ajouté au-delà de son /L déclaré est rejeté pour incohérence de longueur, parce que les offsets de plage d’octets ne seraient plus dignes de confiance.
  • Le responder plafonne le nombre de plages distinctes qu’il honore par requête. Une requête demandant plus de plages fusionnées que le plafond, ou plus d’octets au total que la représentation entière, voit son Range ignoré et est servie par un 200 complet.

Le préfixe de première page est l’offset de fin de première page /E borné à la longueur du fichier, de sorte que FirstPageProber::prefixFraction() rapporte la taille de la récupération initiale relativement au fichier entier — pour un document de nombreuses pages, c’est tout l’intérêt de Fast Web View. La construction de la réponse découpe la chaîne d’octets en mémoire ; le coût est proportionnel aux octets sélectionnés. L’ETag est calculé une fois par document. Mesure avec des documents représentatifs.

Traite l’entrée comme non fiable. LinearizedDocument::fromBytes() valide les invariants de linéarisation avant qu’aucun offset ne soit utilisé. Le responder rejette un contentType contenant des caractères de contrôle pour prévenir l’injection d’en-tête, dérive une frontière multipart dont l’occurrence à l’intérieur du corps est garantie impossible, et fusionne les plages chevauchantes en bornant leur nombre et leur taille totale pour se défendre contre la classe de déni de service par amplification de plage multipart (Apache HTTPD CVE-2011-3192). Ce module ne journalise aucun contenu de document.

La livraison par plage d’octets suit RFC 9110 (HTTP Semantics) — §14 pour les requêtes de plage, §13.1.5 pour If-Range et §15.3.7 pour 416. Le modèle de document linéarisé est l’agencement Fast Web View décrit par ISO 32000-2 Annex F. Le module n’affirme aucun autre identifiant de clause externe au-delà du comportement vérifié par ses tests.

Enterprise ne change pas le comportement de Webview. Enterprise ajoute des fonctionnalités de conformité et d’archivage de palier supérieur documentées séparément ; elles ne sont pas requises pour servir un PDF linéarisé par plages d’octets.

Cette page documente uniquement le comportement observable de l’extérieur et la surface d’API publique prise en charge. Les chemins d’espaces de noms internes, les classes utilitaires, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de tickets sont hors périmètre.