PDF Library for Delphi (PDFlibPas) schrijft Web Capture-commandmetagegevens in de catalog van een document via AddWebCaptureCommand en vult daarmee het /SpiderInfo-dictionary dat ISO 32000-1 §14.10 definieert. Elk command legt de URL vast die is opgehaald, hoe diep de fetch is gegaan en een kleine set flags. Het legt intentie vast — het haalt zelf niets op
Dat onderscheid is precies waarom deze API zo makkelijk verkeerd te lezen is. Een supportengineer overhandigt je een PDF die uit de online documentatie van een leverancier is samengesteld en vraagt waar pagina 40 vandaan komt. Heeft de producent /SpiderInfo ingevuld, dan ligt het antwoord in de catalog: een URL, een crawl-diepte, misschien de POST-body die de pagina heeft opgeleverd. Heeft de producent dat niet gedaan, dan kan geen enkele lezing van de contentstream je dat vertellen. Zulke herkomst vastleggen is goedkoop, structureel precair werk en wordt bijna altijd overgeslagen
Wat registreert het Catalog /SpiderInfo-dictionary eigenlijk?
/SpiderInfo is een Web Capture-informatiedictionary dat aan de documentcatalog hangt en precies twee dingen bevat waar PDF Library for Delphi om geeft: /V, een numerieke Web Capture-databaseversie die de library altijd als 1.0 schrijft, en /C, een array van command-dictionaries. Het versienummer is niet de PDF-versie — het identificeert het Web Capture-datamodel zelf, en 1.0 is de enige waarde die ISO 32000-1 §14.10 definieert. Alles wat interessant is, woont in /C. Elk command-dictionary draagt /URL (het adres dat is aangevraagd), /L (hoeveel niveaus links er vanaf zijn gevolgd) en /F (traversal-flags). Drie optionele keys beschrijven de aanvraag zelf wanneer het geen gewone GET was: /P voor POST-data, /CT voor het contenttype van die data en /H voor extra request-headers. PDF Library for Delphi mapt alle zes op één record, TPDFlibWebCaptureCommand, waarvan de velden URL, Levels, Flags, PostData, ContentType en Headers allemaal AnsiString zijn, behalve de twee Integer-tellers. De aanwezigheid van /SpiderInfo duwt ook de minimumversie van het document omhoog: AddWebCaptureCommand roept EnsureMinVersion('1.3', 'Catalog /SpiderInfo Web Capture command') aan, en de versiedetectieregels in PDF Library for Delphi registreren een bijpassende PDF 1.3-regel, zodat een later teruggelezen document dezelfde ondergrens rapporteert
Waarom moet een Web Capture-command een indirect object zijn?
De enige structurele regel waar mensen over struikelen: een command-dictionary moet als indirect object geregistreerd worden voordat het in /C gaat, want ISO 32000-1 §14.10 specificeert /C als een array van indirecte referenties naar command-dictionaries, niet als een array van dictionaries. Duw je een direct dictionary in die array, dan krijg je iets dat als valide PDF parseert maar als Web Capture-record niets betekent — een viewer of archieftool die de commandlijst doorloopt vindt een inline object waar een referentie hoort, en de entry is simpelweg geen command. PDF Library for Delphi handelt dit binnen AddWebCaptureCommand af: het bouwt het dictionary, geeft het aan OnNewIndObj om een objectnummer en generatienummer te krijgen, wikkelt dat paar in een indirecte referentie en voegt de referentie aan de array toe. Levert OnNewIndObj geen positief objectnummer op, dan geeft de functie 0 terug en blijft de array onaangeroerd. De reden dat de spec op indirectheid staat, is dat één command gedeeld kan worden — dezelfde fetch kan vanuit meer dan één plek in een Web Capture-structuur gerefereerd worden — en gedeelde objecten in PDF zijn per definitie indirecte
Hoe voeg je vanuit Delphi een Web Capture-command toe?
Vul het record volledig in en roep daarna AddWebCaptureCommand aan, dat de 1-based index van het nieuwe command teruggeeft of 0 als validatie het afwees. Volledigheid doet er hier meer dan normaal toe. Delphi initialiseert de managed AnsiString-velden van een lokaal record tot leeg, maar de Integer-velden — Levels en Flags — krijgen die behandeling niet, dus een record dat je vergeet in te vullen arriveert met wat er toevallig op de stack stond. AddWebCaptureCommand vereist dat Levels minstens 1 is en Flags niet-negatief, en een stackgarbage-waarde wordt ofwel botweg afgewezen of, erger, stilzwijgend geaccepteerd als een aannemelijk ogende diepte
var
Lib: TPDFlib;
Command: TPDFlibWebCaptureCommand;
Index: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile('captured-site.pdf', '') = 1 then
begin
// Ken elk veld toe: Levels en Flags zijn gewone Integers en
// dragen stackgarbage mee als je ze overslaat.
Command.URL := 'https://example.com/docs/index.html';
Command.Levels := 2;
Command.Flags := PDF_WEB_CAPTURE_SAME_SITE or PDF_WEB_CAPTURE_SAME_PATH;
Command.PostData := '';
Command.ContentType := '';
Command.Headers := '';
// Geeft de nieuwe 1-based commandindex terug, of 0 bij afwijzing.
Index := Lib.AddWebCaptureCommand(Command);
if Index = 0 then
raise Exception.Create('Web Capture command rejected');
Lib.SaveToFile('captured-site.pdf');
end;
finally
Lib.Free;
end;
end;
De eerste succesvolle aanroep is ook wat de structuur aanmaakt. AddWebCaptureCommand vraagt de interne helper WebCaptureCommands(True) om de command-array, en die helper maakt /SpiderInfo met zijn /V-entry en daarna een lege /C-array alleen als ze ontbreken. Hij is bewust conservatief over wat hij aantreft: heeft de catalog al een /SpiderInfo-key waarvan de waarde geen dictionary is, of een /C-key waarvan de waarde geen array is, dan geeft de helper nil terug in plaats van het bestaande object te overschrijven. Een misvormde geërfde structuur laat de add mislukken; het laat PDF Library for Delphi niet vernietigen wat een vorige producent schreef
De flag-whitelist: drie gedefinieerde bits en niets anders
AddWebCaptureCommand valideert Flags tegen een whitelist en wijst elke waarde af met een bit buiten de drie constanten die PDF Library for Delphi definieert: PDF_WEB_CAPTURE_SAME_SITE = 1 (volg alleen links die op dezelfde site blijven), PDF_WEB_CAPTURE_SAME_PATH = 2 (volg alleen links onder hetzelfde pad) en PDF_WEB_CAPTURE_SUBMIT = 4 (deze aanvraag was een formulierinzending in plaats van een gewone retrieval). Geef je 8 door, of 16, of een toevallige -1, dan geeft de aanroep 0 terug zonder het document aan te raken
Dit is strenger dan PDF-parsers meestal zijn, en dat is opzettelijk. /F is een bitveld zonder zelfbeschrijvende structuur, dus een ongedefinieerde bit is niet te onderscheiden van een corrupte, en een consumer die de waarde later terugleest kan een toekomstige extensie niet van een typo onderscheiden. Bij het schrijven weigeren houdt de dubbelzinnigheid volledig uit het bestand. Let ook op: /P, /CT en /H worden alleen geschreven wanneer de bijbehorende recordvelden niet leeg zijn, dus een gewone GET levert een command-dictionary met drie keys op — /URL, /L, /F — zonder lege-string-placeholders die het vervuilen, precies wat een lezer verwacht bij een aanvraag zonder body
// Een formulierinzending: POST-body plus het contenttype.
Command.URL := 'https://example.com/search';
Command.Levels := 1;
Command.Flags := PDF_WEB_CAPTURE_SUBMIT; // 4
Command.PostData := 'q=iso32000&scope=all';
Command.ContentType := 'application/x-www-form-urlencoded';
Command.Headers := 'Accept-Language: en-US';
if Lib.AddWebCaptureCommand(Command) = 0 then
raise Exception.Create('submission command rejected');
// Same-site plus same-path is een legale combinatie: 1 of 2 = 3.
Command.Flags := PDF_WEB_CAPTURE_SAME_SITE or PDF_WEB_CAPTURE_SAME_PATH;
// Bit 3 is niet gedefinieerd door de spec, dus deze bereikt het bestand nooit.
Command.Flags := 8;
if Lib.AddWebCaptureCommand(Command) <> 0 then
raise Exception.Create('undefined flag bit should have been refused');
Commands teruglezen: één key, twee legale representaties
GetWebCaptureCommand geeft 1 terug bij succes en 0 anderszins, en het moet toleranter zijn dan de writer, want het leest bestanden die andere producenten schreven. Twee asymmetrieën doen er toe. Ten eerste accepteert de reader een command-entry of die nu een indirecte referentie of een direct dictionary is, en derefereert hij wanneer nodig — PDF Library for Delphi schrijft altijd de indirecte vorm die de spec vereist, maar weigeren een slordig bestand te lezen helpt niemand. Ten tweede mag /P ofwel een string ofwel een stream zijn, en die generaliseert ver voorbij Web Capture. ISO 32000-1 laat een enkele key herhaaldelijk ofwel een string ofwel een stream dragen wanneer de waarde willekeurig groot kan zijn — POST-bodies hier, en hetzelfde patroon duikt elders in het formaat op voor metagegevens en ingebedde payloads. De interne ReadString-helper in GetWebCaptureCommand neemt daarom een AllowStream-flag, en alleen de /P-read geeft True door: een streamwaarde wordt via zijn filterketen gedecodeerd en teruggegeven als het veld PostData, terwijl /URL, /CT en /H string-only blijven omdat de spec voor die keys geen stream-alternatief biedt. Schrijf je zelf PDF-leescode, dan is dit de gewoonte die je wilt stelen — check de spec op welke keys dubbelgetypeerd zijn in plaats van van een string uit te gaan
var
Lib: TPDFlib;
Command: TPDFlibWebCaptureCommand;
I, Total, Removed: Integer;
begin
Total := Lib.GetWebCaptureCommandCount;
// Publieke indexering is 1-based, net als de rest van PDF Library for Delphi.
for I := 1 to Total do
if Lib.GetWebCaptureCommand(I, Command) = 1 then
Writeln(Format('%s levels=%d flags=%d post=%d bytes',
[Command.URL, Command.Levels, Command.Flags, Length(Command.PostData)]));
// Verwijdert het hele /SpiderInfo-dictionary en rapporteert hoeveel
// commands zijn verwijderd; geeft 0 terug als er niets te wissen viel.
Removed := Lib.ClearWebCaptureCommands;
Writeln(Format('cleared %d command(s)', [Removed]));
end;
ClearWebCaptureCommands telt de commands en zuivert daarna de hele /SpiderInfo-key uit de catalog in plaats van alleen /C leeg te maken. Een /SpiderInfo met een lege command-array achterlaten zou beweren dat het document Web Capture-herkomst heeft en dat er niets is opgehaald, een andere en onware claim dan helemaal geen herkomst vastleggen. Hetzelfde instinct geldt algemeen voor catalogmetagegevens — een lege shell-dictionary is een statement, geen neutrale rest. Audit je wat een document al claimt voordat je het herschrijft, dan past het introspectiepatroon uit uitgaande outline- en annotatie-actions uit een bestaande PDF teruglezen natuurlijk bij een read-then-clear-pass hier
Metagegevens schrijven is geen Web Capture implementeren
Wees eerlijk tegen jezelf over de scope: AddWebCaptureCommand slaat een record op van een beoogde of voltooide fetch, en dat is alles. PDF Library for Delphi voert geen HTTP-requests uit, volgt de links niet die je Levels-waarde beschrijft en honoreert de SAME_SITE- of SAME_PATH-flags niet — die flags documenteren hoe een crawler zich gedroeg, het zijn geen instructies die de library uitvoert. Je eigen fetch-code, wat die ook is, blijft volledig jouw probleem
Nog twee stukken van ISO 32000-1 §14.10 vallen buiten deze API. Web Capture definieert contentsets — de /Contents-structuren die individuele paginastukken terugkoppelen aan het bronmateriaal waar ze vandaan komen — en een name tree die digitale identificaties op die contentsets mapt. PDF Library for Delphi onderhoudt geen van beide. Wat je krijgt is de commandlijst: het antwoord op het hoogste niveau op "waar is dit document vandaan gehaald en hoe diep ging de crawl", wat voor herkomst, audit en archief-intake meestal het deel is waar iemand daadwerkelijk naar vraagt. Wil je dat de URLs voor lezers klikbaar zijn in plaats van alleen voor auditors vastgelegd, dan is dat een aparte klus — zie GoToR-, GoToE- en Launch-actions bouwen voor de actiekant en triggers voor document- en paginalifecycle-actions voor afvuurgedrag bij openen of sluiten. De Catalog /SpiderInfo-commandmetagegevens zoals hier beschreven maken deel uit van PDF Library for Delphi, de native PDF-library voor Delphi en C++Builder