Aller au contenu
getnextpdf.com

stabilité: Expérimental

PageBackfill : tampon de page conservé

Prévisualisation opt-in. Le tampon de page conservé est désactivé par défaut. Lorsqu’il est désactivé, le writer est le sérialiseur en streaming qu’il a toujours été — octet pour octet identique. Ne l’activez que lorsque vous avez réellement besoin de dessiner sur une page antérieure, et lisez d’abord la liste fail-closed ci-dessous.

Par défaut, le writer diffuse les pages en streaming et les vide dans l’ordre ; une fois qu’une page est vidée, elle ne peut plus être dessinée. Le tampon de page conservé est l’opt-in qui retient les pages vidées afin qu’une page précédemment vidée puisse être remplie a posteriori — dessinée sur une page antérieure — avant que le document ne soit sérialisé. L’usage classique est un total ou une boîte de synthèse que vous ne pouvez placer qu’après la mise en page des pages ultérieures.

Fenêtre de terminal
composer require nextpdf/core:^3

Le tampon de page conservé est livré dans le paquet core. Config::withRetainedPageBuffer() et les méthodes de remplissage a posteriori de Document sont @since 6.1.0. La valeur par défaut reste le writer en streaming. ADR-037, qui avait précédemment reporté cette capacité, est désormais consignée comme implémentée.

Config::withRetainedPageBuffer() fait entrer un document dans le mode de pages conservées. Une fois activé, Document::setActiveBackfillPage(int $pageIndex) redirige le dessin vers une page antérieure déjà vidée ; Document::endPageBackfill() ramène le dessin à la position d’ajout normale. Le contenu que vous écrivez entre les deux appels atterrit sur la page antérieure. Le tampon retient les pages jusqu’à save(), de sorte que le remplissage a posteriori est appliqué avant l’écriture de la table de références croisées et du trailer (ISO 32000-2 §7.5).

Le remplissage a posteriori est une opération à accès aléatoire, et plusieurs fonctionnalités de document supposent des octets en ajout seul, diffusés en streaming. Le tampon de page conservé refuse de se combiner avec n’importe laquelle d’entre elles, indépendamment de l’ordre et avant la sérialisation, afin qu’il ne puisse jamais casser silencieusement une signature ou une revendication de conformité :

  • Une signature numérique.
  • Le PDF balisé (arbre de structure).
  • PDF/A.
  • La linéarisation.
  • L’empaquetage en flux d’objets.
  • Le chiffrement.
  • Le mode de rendu CSS Safe.

Un budget d’octets non compressés par document plafonne la quantité que le tampon peut retenir ; un document qui le dépasse échoue durement plutôt que de consommer une mémoire non bornée. Le défaut streaming échoue toujours en mode fail-closed dès qu’un appelant tente un basculement en accès aléatoire sans l’opt-in — activer le tampon est le seul moyen d’obtenir le remplissage a posteriori, et il est incompatible par construction avec les fonctionnalités ci-dessus.

SymboleEmplacementRôle
Config::withRetainedPageBuffer(bool $enabled = true): selfsrc/Core/Config.phpFait entrer un document dans le tampon de page conservé.
Document::setActiveBackfillPage(int $pageIndex): staticsrc/Core/Document.phpRedirige le dessin vers une page antérieure déjà vidée.
Document::endPageBackfill(): staticsrc/Core/Document.phpRamène le dessin à la position d’ajout normale.

Une tentative de remplissage a posteriori qui viole une combinaison refusée lève une exception de configuration typée à la frontière, pas un document corrompu.

Réservez un emplacement sur la page un, remplissez le reste du document, puis remplissez a posteriori l’emplacement réservé avec une valeur calculée à la fin.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Config;
use NextPDF\Core\Document;
$config = (new Config())->withRetainedPageBuffer();
$doc = Document::createStandalone($config);
$doc->addPage(); // page 0 — leaves room for a grand total
$doc->writeHtml('<h1>Invoice</h1>');
$doc->addPage(); // page 1 — line items
$doc->writeHtml('<p>Line items…</p>');
$total = 1234.56; // computed after laying out the items
$doc->setActiveBackfillPage(0); // draw back onto page 0
$doc->writeHtml('<p>Grand total: ' . number_format($total, 2) . '</p>');
$doc->endPageBackfill();
$doc->save(__DIR__ . '/invoice.pdf');

Gardez le tampon désactivé pour tout document signé, balisé, PDF/A, linéarisé, chiffré ou en flux d’objets — ce sont exactement les combinaisons que le tampon refuse. Choisissez un chemin explicitement.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Config;
use NextPDF\Core\Document;
function renderReport(bool $needsBackfill, bool $mustBeSigned): Document
{
if ($needsBackfill && $mustBeSigned) {
// The buffer refuses to combine with signing. Resolve the requirement
// before building: pre-compute the value, or sign a separate pass.
throw new \LogicException('Back-fill and signing are mutually exclusive.');
}
$config = new Config();
if ($needsBackfill) {
$config = $config->withRetainedPageBuffer();
}
return Document::createStandalone($config);
}
  • Désactivé, c’est octet pour octet identique. Avec le tampon désactivé, le writer diffuse en streaming comme avant.
  • Mutuellement exclusif avec la signature, le balisage, PDF/A, la linéarisation, les flux d’objets, le chiffrement et le mode de rendu CSS Safe. Le refus est indépendant de l’ordre et se déclenche avant la sérialisation. Planifiez le document pour un mode ou pour l’autre.
  • Un budget d’octets échoue durement. Le tampon conservé est borné ; un document qui dépasse le budget d’octets non compressés échoue plutôt que de croître sans limite.
  • Appariez les appels. Chaque setActiveBackfillPage() devrait être apparié à un endPageBackfill() afin que le contenu ultérieur s’ajoute normalement.
  • Le défaut streaming refuse l’accès aléatoire. Sans l’opt-in, un basculement en accès aléatoire échoue en mode fail-closed. Le tampon est le seul chemin pris en charge.

Le tampon de page conservé échange de la mémoire contre la capacité de remplissage a posteriori : il retient les pages vidées jusqu’à save(), borné par le budget d’octets non compressés par document. Le profil mémoire plat du writer en streaming ne s’applique qu’avec le tampon désactivé. Le performance_budget (wall_ms: 1500, peak_mb: 128) reflète le plafond mémoire plus élevé du chemin conservé.

Le tampon de page conservé n’élargit pas la surface d’entrée ; il change le moment où les octets sont sérialisés, pas ce qui est ingéré. Son refus de se combiner avec le chiffrement et la signature est une propriété de sûreté : un remplissage a posteriori ne peut jamais altérer des octets signés ou chiffrés après coup, car les deux ne peuvent pas être activés ensemble. Le budget d’octets borne la mémoire face à un document hostile.

ÉnoncéSpécificationClause
Le writer sérialise le corps, la structure de références croisées et le trailer au moment de l’enregistrement.ISO 32000-2§7.5

Il s’agit d’une capacité de prévisualisation. NextPDF refuse le tampon de remplissage a posteriori pour les documents signés, balisés, PDF/A, linéarisés, chiffrés et en flux d’objets, de sorte qu’il ne fait aucune revendication de conformité pour ces profils via ce chemin. Aucun texte de norme n’est reproduit.

L’adaptateur de compatibilité TCPDF expose cette capacité comme une extension de constructeur. Construisez l’adaptateur avec retainedPageBuffer: true, puis un appel setPage() ou lastPage() qui cible une page antérieure délègue au remplissage a posteriori du core au lieu de lever UnsupportedFeatureException du streaming. Cet argument de constructeur est une extension NextPDF, pas une parité avec le TCPDF historique — le TCPDF historique n’a pas un tel indicateur. Les mêmes refus fail-closed s’appliquent. Consultez la page tampon de page conservé de l’adaptateur compat pour les détails côté adaptateur.