PDF Library for Delphi (PDFlibPas) zapisuje metadata Web Capture příkazů do katalogu dokumentu přes AddWebCaptureCommand a plní tak slovník /SpiderInfo definovaný v ISO 32000-1 §14.10. Každý příkaz zaznamenává URL, které bylo staženo, kolik úrovní hluboko stahování proběhlo a malou sadu příznaků. Zaznamenává záměr – nic nestahuje
Právě tento rozdíl je celý důvod, proč se toto API snadno čte špatně. Podpůrný inženýr vám podá PDF poskládané z online dokumentace dodavatele a ptá se, odkud se vzala strana 40. Pokud producent vyplnil /SpiderInfo, odpověď sedí v katalogu: URL, hloubka procházení, možná i POST tělo, které stránku vytvořilo. Pokud ne, žádné čtení content streamu vám to neřekne. Zápis takového původu je levný, strukturálně úzkostlivý a téměř vždy se vynechává
Co slovník katalogu /SpiderInfo vlastně zaznamenává?
/SpiderInfo je informační slovník Web Capture visící na katalogu dokumentu a obsahuje přesně dvě věci, které PDF Library for Delphi řeší: /V, číselnou verzi Web Capture databáze, kterou knihovna vždy zapisuje jako 1.0, a /C, pole slovníků příkazů. Číslo verze není verzí PDF – identifikuje samotný datový model Web Capture a 1.0 je jediná hodnota, kterou ISO 32000-1 §14.10 definuje. Vše zajímavé žije v /C. Každý slovník příkazu nese /URL (adresu, která byla vyžádána), /L (kolik úrovní odkazů se z ní prošlo) a /F (příznaky procházení). Tři volitelné klíče popisují samotný požadavek, když nešlo o obyčejné GET: /P pro POST data, /CT pro typ obsahu těchto dat a /H pro další hlavičky požadavku. PDF Library for Delphi mapuje všech šest do jediného záznamu TPDFlibWebCaptureCommand, jehož pole jsou URL, Levels, Flags, PostData, ContentType a Headers, všechna typu AnsiString kromě dvou čítačů Integer. Přítomnost /SpiderInfo také posouvá minimální verzi dokumentu: AddWebCaptureCommand volá EnsureMinVersion('1.3', 'Catalog /SpiderInfo Web Capture command') a pravidla detekce verzí v PDF Library for Delphi registrují odpovídající pravidlo PDF 1.3, takže dokument načtený později hlásí stejnou spodní hranici
Proč musí být Web Capture příkaz nepřímým objektem
Jediné strukturální pravidlo, o které lidé zakopnou: slovník příkazu se musí zaregistrovat jako nepřímý objekt, než se dostane do /C, protože ISO 32000-1 §14.10 definuje /C jako pole nepřímých odkazů na slovníky příkazů, ne jako pole slovníků. Vložíte-li do pole přímý slovník, dostanete něco, co se parsuje jako platné PDF a jako záznam Web Capture to neznamená nic – prohlížeč nebo archivní nástroj procházející seznam příkazů najde vložený objekt tam, kde má být odkaz, a položka prostě není příkazem. PDF Library for Delphi to řeší uvnitř AddWebCaptureCommand: postaví slovník, předá ho OnNewIndObj, aby získala číslo objektu a číslo generace, tento pár zabalí do nepřímého odkazu a odkaz připojí na konec pole. Pokud OnNewIndObj nevyrobí kladné číslo objektu, funkce vrátí 0 a pole zůstane nedotčené. Důvod, proč specifikace trvá na nepřímém odkazu, je ten, že jeden příkaz lze sdílet – stejné stažení může být odkazováno z více míst struktury Web Capture – a sdílené objekty v PDF jsou z definice právě ty nepřímé
Jak přidáte Web Capture příkaz z Delphi?
Vyplňte záznam úplně a pak zavolejte AddWebCaptureCommand, která vrátí index nového příkazu číslovaný od jedničky nebo 0, pokud ho validace odmítla. Úplnost tu záleží víc než obvykle. Delphi inicializuje spravovaná pole AnsiString lokálního záznamu na prázdné, ale pole Integer – Levels a Flags – takové zacházení nedostanou, takže záznam, který zapomenete vyplnit, dorazí s tím, co zůstalo na zásobníku. AddWebCaptureCommand vyžaduje Levels alespoň 1 a Flags nezáporné a hodnota ze zásobníkového odpadu bude buď rovnou odmítnuta, nebo hůř – potichu přijata jako věrohodně vypadající hloubka
var
Lib: TPDFlib;
Command: TPDFlibWebCaptureCommand;
Index: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile('captured-site.pdf', '') = 1 then
begin
// Přiřaďte každé pole: Levels a Flags jsou obyčejné Integery a
// pokud je přeskočíte, nesou zásobníkový odpad.
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 := '';
// Vrací nový index příkazu od jedničky, nebo 0 při odmítnutí.
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;
Strukturu vytvoří až první úspěšné volání. AddWebCaptureCommand si nechá pole příkazů od interní pomocné funkce WebCaptureCommands(True) a ta vytvoří /SpiderInfo s položkou /V a pak prázdné pole /C, jen pokud chybí. K tomu, co najde, je záměrně konzervativní: pokud katalog už má klíč /SpiderInfo, jehož hodnota není slovník, nebo klíč /C, jehož hodnota není pole, pomocná funkce vrátí nil, místo aby přepsala existující objekt. Chybná zděděná struktura způsobí, že přidání selže; PDF Library for Delphi kvůli ní nezničí, co napsal předchozí producent
Whitelist příznaků: tři definované bity a nic víc
AddWebCaptureCommand validuje Flags proti whitelistu a odmítá každou hodnotu s nastaveným bitem mimo tři konstanty, které PDF Library for Delphi definuje: PDF_WEB_CAPTURE_SAME_SITE = 1 (následovat jen odkazy zůstávající na stejné stránce), PDF_WEB_CAPTURE_SAME_PATH = 2 (následovat jen odkazy pod stejnou cestou) a PDF_WEB_CAPTURE_SUBMIT = 4 (tento požadavek byl odesláním formuláře, ne obyčejným stažením). Předáte 8, 16 nebo náhodné -1 a volání vrátí 0, aniž by sáhlo na dokument
To je přísnější, než bývají PDF parsery, a záměrně. /F je bitové pole bez sebepopisující struktury, takže nedefinovaný bit je k nerozeznání od poškozeného a konzument, který hodnotu později čte zpět, nemá jak rozlišit budoucí rozšíření od překlepu. Odmítnutí při zápisu drží nejednoznačnost úplně mimo soubor. Všimněte si také, že /P, /CT a /H se zapisují jen když odpovídající pole záznamu nejsou prázdná, takže obyčejné GET vyrobí slovník příkazu se třemi klíči – /URL, /L, /F – bez prázdných placeholderů, které by ho zaneřádily, což čtečka parsující soubor u požadavku bez těla očekává
// Odeslání formuláře: POST tělo plus jeho typ obsahu.
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 je legální kombinace: 1 nebo 2 = 3.
Command.Flags := PDF_WEB_CAPTURE_SAME_SITE or PDF_WEB_CAPTURE_SAME_PATH;
// Bit 3 specifikace nedefinuje, takže tenhle se k souboru nikdy nedostane.
Command.Flags := 8;
if Lib.AddWebCaptureCommand(Command) <> 0 then
raise Exception.Create('undefined flag bit should have been refused');
Čtení příkazů zpět: jeden klíč, dvě legální reprezentace
GetWebCaptureCommand vrací 1 při úspěchu a jinak 0 a musí být tolerantnější než zapisovač, protože čte soubory, které napsali jiní producenti. Záleží na dvou asymetriích. Zaprvé, čtečka přijme položku příkazu, ať jde o nepřímý odkaz nebo přímý slovník, a v případě potřeby ho dereferencuje – PDF Library for Delphi vždy zapisuje nepřímou podobu, kterou specifikace vyžaduje, ale odmítat číst nepořádný soubor nikomu nepomůže. Zadruhé, /P smí být buď řetězec, nebo stream, a toto se zobecňuje daleko za Web Capture. ISO 32000-1 opakovaně dovoluje jedinému klíči držet buď řetězec, nebo stream, když hodnota může být libovolně velká – POST těla tady a stejný vzor se v tomto formátu objevuje i jinde u metadat a vložených dat. Interní pomocná funkce ReadString v GetWebCaptureCommand proto bere příznak AllowStream a hodnotu True předává jen čtení /P: hodnota streamu se dekóduje přes řetězec filtrů a vrátí se jako pole PostData, zatímco /URL, /CT a /H zůstávají jen řetězce, protože jim specifikace alternativu streamu nenabízí. Pokud píšete vlastní kód pro čtení PDF, tohle je zvyk, který se vyplatí ukrást – zkontrolujte ve specifikaci, které klíče jsou dvojího typu, místo abyste předpokládali řetězec
var
Lib: TPDFlib;
Command: TPDFlibWebCaptureCommand;
I, Total, Removed: Integer;
begin
Total := Lib.GetWebCaptureCommandCount;
// Veřejné indexování je od jedničky, jako ve zbytku 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)]));
// Odstraní celý slovník /SpiderInfo a hlásí, kolik
// příkazů bylo zahozeno; vrací 0, když nebylo co mazat.
Removed := Lib.ClearWebCaptureCommands;
Writeln(Format('cleared %d command(s)', [Removed]));
end;
ClearWebCaptureCommands spočítá příkazy a pak odstraní z katalogu celý klíč /SpiderInfo, místo aby jen vyprázdnil /C. Nechat za sebou /SpiderInfo s prázdným polem příkazů by tvrdilo, že dokument má původ ve Web Capture a že se nic nestahovalo, což je jiné a falešné tvrzení než nemít zaznamenaný žádný původ. Stejný instinkt platí obecně pro metadata katalogu – prázdný skořápkový slovník je tvrzení, ne neutrální zbytek. Pokud před přepisem auditováte, co dokument už tvrdí, vzor introspekce z čtení akcí osnov a anotací z existujícího PDF se tady přirozeně snoubí s průchodem nejdřív přečti a pak smaž
Zápis metadat není implementace Web Capture
Buďte k sobě o rozsahu upřímní: AddWebCaptureCommand ukládá záznam zamýšleného nebo dokončeného stažení, a to je všechno. PDF Library for Delphi neprovádí HTTP požadavky, nenásleduje odkazy, které popisuje vaše hodnota Levels, a nerespektuje příznaky SAME_SITE ani SAME_PATH – tyto příznaky jsou dokumentací toho, jak se choval nějaký crawler, ne instrukcemi, které by knihovna vykonávala. Váš vlastní kód pro stahování, ať je jakýkoli, zůstává zcela vaším problémem
Další dvě části ISO 32000-1 §14.10 jsou také mimo toto API. Web Capture definuje content sety – struktury /Contents, které vážou jednotlivé kusy stránek zpět ke zdrojovému materiálu, ze kterého vznikly – a name tree, který mapuje digitální identifikátory na tyto content sety. PDF Library for Delphi nespravuje ani jedno. Dostanete seznam příkazů: odpověď na nejvyšší úrovni na otázku, odkud byl tento dokument stažen a jak hluboko procházení šlo, což je pro účely původu, auditu a archivního příjmu obvykle to, na co se každý skutečně ptá. Pokud potřebujete, aby URL byla pro čtenáře klikatelná, ne jen zaznamenaná pro auditory, je to samostatná práce – pro akční stránku viz tvorba akcí GoToR, GoToE a Launch a pro spouštění chování při otevření či zavření viz spouštěče akcí životního cyklu dokumentu a stránky. Metadata Web Capture příkazů katalogu /SpiderInfo, jak jsou popsána zde, jsou součástí PDF Library for Delphi, nativní PDF knihovny pro Delphi a C++Builder