Ga naar inhoud

Het pijplijnmodel

Spec: ISO 32000-2, §7.5 Evidence: Code-backed

Een NextPDF-document wordt niet in één ondoorzichtige stap geproduceerd. Het doorloopt een klein aantal expliciete fasen: een façade die de intentie vastlegt, een contentlaag die die intentie omzet in een model, en een writer die dat model serialiseert tot een conforme PDF. Deze pagina legt die opzet uit en waarom dit de juiste vorm voor de engine is.

Het PDF-bestandsformaat is zelf gelaagd opgebouwd: een header, een hoofddeel met objecten, een cross-referencetabel en een trailer. Een writer moet al die onderdelen consistent samenstellen. Als de engine die dit opbouwt één verstrengelde procedure is, brengt elke wijziging elke uitvoer in gevaar. De enige manier om vertrouwen te krijgen is dan volledige documenten renderen en ze visueel inspecteren; dat is traag, laat en weinig overtuigend.

Een expliciete pijplijn draait dat om. Elke fase heeft één taak en een getypeerde grens, zodat je over een wijziging kunt redeneren en die wijziging kunt testen in de fase die ze raakt, niet pas aan het einde van het bestand. De architectuur is in de eerste plaats een keuze voor testbaarheid en uitbreidbaarheid.

  • Het publieke startpunt is een Document-façade. Het is een fluente, eenmalig te gebruiken, worker-veilige builder die vastlegt wat je wilt, niet hoe het wordt geserialiseerd.
  • De façade delegeert naar ongeveer twee dozijn gerichte concern-traits (tekstuitvoer, tekenen, pagina’s, beveiliging, navigatie enzovoort), elk met één verantwoordelijkheid, niet één gigantische klasse.
  • Content komt binnen via een van twee paden: direct tekenen (grafische primitieven) of de HTML/CSS-engine. Beide produceren hetzelfde interne documentmodel.
  • Een aparte PDF-writer serialiseert dat model en kiest een PDF 1.4 / 1.7 / 2.0-strategie. Het produceren van een geldige bestandsstructuur gebeurt hier en nergens anders.
  • State met een lange levensduur (lettertype- en afbeeldingsregisters) is procesgebonden en gedeeld; per-request-state (het document) wordt voor elke request opnieuw aangemaakt en nooit hergebruikt. Die grens is expliciet, en daardoor blijven worker-runtimes veilig.

De helderste manier om het model te zien is een document te volgen van aanroep tot bytes.

  1. Document facade Fluent, use-once builder; records intent via concern traits.
  2. Content production Direct drawing or the HTML/CSS engine — both build one document model.
  3. Document model Accumulated pages, content, and resources held as typed state.
  4. PDF writer Serialises the model; selects a PDF 1.4 / 1.7 / 2.0 strategy.
  5. Conforming PDF Header, object body, cross-reference table, trailer.
Het pad van een document door NextPDF: elke fase heeft één verantwoordelijkheid en een getypeerde grens, zodat erover geredeneerd en in isolatie getest kan worden.

Twee ontwerpkeuzes maken dit meer dan een diagram.

De façade is samengesteld, niet monolithisch. Document implementeert niet elke functie zelf; elk gebied wordt gedelegeerd aan een gerichte concern-trait: tekstuitvoer, tekenen, pagina’s, beveiliging, typografie, navigatie, transacties enzovoort. Een nieuwe documentmethode hoort thuis in de trait voor het betreffende gebied, niet op de façade zelf. De klasse die je aanroept blijft klein en de verantwoordelijkheden blijven gescheiden.

Alleen de writer beheert de bestandsstructuur. Contentproductie beslist welke markeringen en objecten bestaan; de writer beslist hoe die een geldig PDF-bestand worden, inclusief welke versiestrategie van toepassing is. Die scheiding wordt afgedwongen als architectuurregel: lay-out- en contentcode genereert geen definitieve bestandsstructuur, en de writer neemt geen lay-outbeslissingen. Het voordeel is dat de vraag „is de uitvoer een geldige PDF?” precies één plek heeft waar die wordt getest.

De levensduurgrens is onderdeel van het model, geen bijzaak. Lettertype- en afbeeldingsregisters bestaan zolang het proces leeft en worden gedeeld tussen requests; het document, de weergavecontext en de writer worden per request aangemaakt en weer opgeruimd. In een worker-runtime is dat onderscheid het verschil tussen veilig hergebruik en corruptie tussen requests. Daarom is het vastgelegd in de architectuur en niet overgelaten aan discipline.

Deze pagina is Evidence: Code-backed . De fasen komen overeen met de daadwerkelijke structuur in de core-repository:

  • De façade en de delegatie ervan zijn src/Core/Document.php plus de concern-traits in src/Core/Concerns/ (tekstuitvoer, uitvoer, tekenen, pagina’s, beveiliging, typografie, navigatie, transacties en meer — elk met één verantwoordelijkheid).
  • De twee contentpaden zijn de HTML/CSS-engine (src/Html/) en direct tekenen (src/Graphics/), die beide het interne model voeden.
  • Serialisatie en de PDF-versiestrategie bevinden zich in src/Writer/ (PdfWriter.php, met expliciete PDF 1.4 / 1.7 / 2.0-strategieklassen).
  • De grens tussen proceslevensduur en per-request-state is het worker-veilige ontwerp dat is vastgelegd in het architectuuroverzicht en wordt gedemonstreerd door het meegeleverde worker-factory-voorbeeld, dat een FontRegistry en ImageRegistry deelt tussen requests terwijl het elk Document telkens opnieuw aanmaakt.

De eindvorm ligt vast in het formaat. De uitvoer van de writer moet een header, een hoofddeel met objecten, een cross-referencetabel en een trailer bevatten, conform Spec: ISO 32000-2, §7.5 . Door die verplichting in één fase te concentreren, kan de rest van de engine gericht blijven op content in plaats van op het samenstellen van de bestandsstructuur.

De taak van de façade is de intentie ook als intentie te laten lezen. Het contentpad en de writer blijven onzichtbaar op de aanroepplek:

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone(); // facade
$doc->setTitle('Quarterly Report'); // metadata concern
$doc->addPage(); // pages concern
$doc->setFont('helvetica', 'B', 16); // typography concern
$doc->cell(0, 12, 'Summary', newLine: true); // text-output concern
$doc->writeHtml('<p>Generated in-process.</p>'); // HTML content path
$doc->save(__DIR__ . '/report.pdf'); // writer stage

Elke aanroep komt in een ander concern terecht. Twee verschillende contentpaden voeden hetzelfde model. Precies één fase — save() — zet het model om in bestandsbytes. Niets op de aanroepplek hoeft te weten hoe de cross-referencetabel wordt opgebouwd.

Een veelvoorkomende misvatting is dat „pijplijn” een streaming-push-API impliceert die je fase voor fase aan elkaar koppelt, zoals een Unix-pipe. Dat is niet zo. De pijplijn is hier een architecturale decompositie: fasen met enkelvoudige verantwoordelijkheden en getypeerde grenzen. Je programmeert nog steeds tegen een fluente façade. De fasen zijn de manier waarop de engine wordt gebouwd en getest, niet een transportmechanisme dat je met de hand samenstelt.

Een verwante fout is aannemen dat de façade de engine is. De façade is het startpunt. Het echte werk is verdeeld over concern-traits, twee contentpaden en een writer. Die verdeling is precies de reden waarom één functiewijziging niet elke uitvoer in gevaar brengt.

Deze pagina beschrijft de vorm van de pijplijn, niet de interne API van een afzonderlijke fase. De exacte inventaris van concern-traits, de selectieregels voor de writer-strategie en de velden van het contentmodel worden bepaald door de code en de referentie, niet door deze uitleg. Het precieze aantal traits is een implementatiedetail dat kan veranderen zonder het model te wijzigen. Deze pagina behandelt niet de interne fasen van de HTML-engine (een afzonderlijk onderwerp) of het streaming- en geheugengedrag van de writer (eveneens afzonderlijk). De structurele beweringen zijn juist op de reviewdatum van deze pagina; de gezaghebbende bronnen zijn src/Core/, src/Html/, src/Graphics/ en src/Writer/ van de core-repository.

Het pijplijnmodel is in alle edities identiek; edities voegen mogelijkheden toe binnen bestaande fasen, geen nieuwe fasen:

Pipeline model — edition availability
Edition Availability
Core Core implementeert de volledige façade → content → writer-pijplijn.
Pro Pro voegt mogelijkheden toe binnen bestaande fasen, geen nieuwe fasen.
Enterprise Enterprise voegt mogelijkheden toe binnen bestaande fasen, geen nieuwe fasen.
  • Façade — het publieke Document-startpunt: een fluente, eenmalig te gebruiken builder die de intentie vastlegt en delegeert naar concern-traits.
  • Concern-trait — een gerichte PHP-trait die de façade samenstelt en verantwoordelijk is voor één functiegebied (tekstuitvoer, tekenen, pagina’s, beveiliging enzovoort).
  • Contentpad — een van de twee manieren waarop content in het model komt: direct tekenen of de HTML/CSS-engine.
  • Documentmodel — de interne, getypeerde verzameling van pagina’s, content en resources van de engine vóór serialisatie.
  • Writer-fase — de component die het model serialiseert tot een geldige PDF en daarbij een PDF 1.4 / 1.7 / 2.0-strategie kiest.
  • Worker-veilig — zo ontworpen dat state met proceslevensduur veilig wordt gedeeld terwijl per-request-state telkens opnieuw wordt aangemaakt en nooit hergebruikt.