Súborové prílohy PDF sú uložené v strome vložených súborov dokumentu, čo je štruktúra, ktorú väčšina prehliadačov zobrazuje ako panel so sponkou alebo bočný panel príloh. V kóde Delphi sprístupňuje PDFium VCL tento strom prostredníctvom malej sady indexovaných vlastností na TPdf: prechádzate ich pomocou celočíselného indexu, čítate názvy a polia bajtov, vytvárate nové sloty a odstraňujete existujúce. Rozhranie API je úzke; pred písaním produkčného kódu je potrebné poznať len niekoľko obmedzení poradia a jedno pravidlo sanitácie.
Čítanie príloh z otvoreného dokumentu
Vlastnosť AttachmentCount udáva počet vložených súborov, ktoré dokument deklaruje. Hodnota sa číta priamo z interného volania PDFium, takže odráža iba to, čo PDF skutočne obsahuje. Vlastnosť AttachmentName[Index] vracia zobrazovaný názov ako reťazec WString a Attachment[Index] poskytuje surové bajty vo forme poľa TBytes. Obe vlastnosti sú indexované od nuly. Dokument musí byť otvorený (Pdf.Active = True), skôr než začnete tieto vlastnosti dopytovať; ich volanie na zatvorenom dokumente vráti nulu alebo prázdny výsledok bez vyvolania výnimky.
Jedna vec, na ktorú netreba zabúdať: Attachment[Index] pri každom čítaní alokuje a vracia kompletný obsah súboru. Ak dokument obsahuje veľký vložený súbor, prechádzanie všetkých príloh na vytvorenie zoznamu znamená platiť náklady na túto alokáciu pri každom volaní. Ak potrebujete názvy iba na účely zobrazenia, najprv prečítajte AttachmentName a načítanie bajtov odložte dovtedy, kým používateľ o súbor skutočne nepožiada.
procedure ListAttachments(Pdf: TPdf);
var
I: Integer;
Data: TBytes;
begin
if not Pdf.Active then
Exit;
for I := 0 to Pdf.AttachmentCount - 1 do
begin
Data := Pdf.Attachment[I];
Writeln(Format('%d: %s (%d bytes)',
[I, Pdf.AttachmentName[I], Length(Data)]));
end;
end;
Exportovanie prílohy na disk
K dispozícii nie je žiadna pomocná funkcia SaveAttachment. Prečítate bajty a zapíšete ich tam, kam potrebujete, čím sa konštrukcia cesty a jej sanitácia prenáša výhradne na váš kód. To je dôležité, najmä ak názvy príloh pochádzajú z nedôveryhodných dokumentov. Názvy príloh v PDF sú reťazce uložené vo vnútri súboru; môžu obsahovať oddeľovače ciest, vizuálne podobné znaky Unicode a iné znaky, ktoré spôsobia neočakávané výsledky, ak ich odovzdáte priamo do TFileStream.Create. Pred vytvorením akejkoľvek výstupnej cesty vždy preveďte názov cez ExtractFileName a zvážte odmietnutie názvov, ktoré začínajú bodkou alebo obsahujú znaky mimo očakávaného systémového štandardu.
Pole bajtov vrátené vlastnosťou Attachment[Index] vlastní volajúci. Zapíšte ho pomocou štandardného TFileStream a môžete s ním narábať podľa potreby, vrátane kontroly prvých niekoľkých bajtov na overenie skutočného formátu súboru namiesto slepej dôvery deklarovanému názvu.
procedure ExtractAttachment(Pdf: TPdf; Index: Integer; const OutputDir: string);
var
SafeName: string;
OutPath: string;
Data: TBytes;
FS: TFileStream;
begin
SafeName := ExtractFileName(Pdf.AttachmentName[Index]);
if SafeName = '' then
SafeName := Format('attachment_%d', [Index]);
OutPath := IncludeTrailingPathDelimiter(OutputDir) + SafeName;
Data := Pdf.Attachment[Index];
FS := TFileStream.Create(OutPath, fmCreate);
try
if Length(Data) > 0 then
FS.WriteBuffer(Data[0], Length(Data));
finally
FS.Free;
end;
end;
Pridávanie príloh a dvojkrokový zápis
Vytvorenie prílohy vyžaduje dve volania, nie jedno. Metóda CreateAttachment(Name) zaregistruje nový slot v strome vložených súborov a pri úspechu vráti True. Tento slot je na začiatku prázdny. Potom priradíte obsah zápisom do vlastnosti Attachment[AttachmentCount - 1], čím zacielite na naposledy vytvorenú položku. Ak CreateAttachment vráti False, slot nebol vytvorený a priradenie by poškodilo prílohu na indexe, ktorý je momentálne posledný.
Po úprave zoznamu príloh existujú zmeny iba v pamäti. Zavolajte SaveAs na zápis nového súboru s aktualizovaným stromom vložených súborov. PDFium VCL momentálne nepodporuje ukladanie späť do toho istého súboru, ktorý je otvorený, pretože engine drží popisovač na čítanie zdroja. Štandardným vzorom pre aktualizáciu na mieste je uloženie do dočasnej cesty, zatvorenie dokumentu, odstránenie alebo premenovanie originálu a následné presunutie dočasného súboru na pôvodné miesto a jeho opätovné otvorenie.
procedure AddFileAttachment(Pdf: TPdf; const FilePath: string);
var
FS: TFileStream;
Data: TBytes;
AttachName: string;
begin
if not Pdf.Active then
Exit;
FS := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyWrite);
try
SetLength(Data, FS.Size);
if FS.Size > 0 then
FS.ReadBuffer(Data[0], FS.Size);
finally
FS.Free;
end;
AttachName := ExtractFileName(FilePath);
if Pdf.CreateAttachment(AttachName) then
Pdf.Attachment[Pdf.AttachmentCount - 1] := Data;
end;
Informácie o type prílohy
Okrem názvu a obsahu bajtov vracia vlastnosť AttachmentType[Index] reťazec typu MIME uložený v slovníku vloženého súboru PDF, ak bol zaznamenaný pri pôvodnom priložení súboru. Mnohé generátory nechávajú toto pole prázdne alebo ho nastavujú na všeobecnú hodnotu ako application/octet-stream, so zrejme sa naň nemôžete spoliehať pri detekcii formátu v produkčnom systéme. Pre spoľahlivú identifikáciu prečítajte prvých niekoľko bajtov obsahu a skontrolujte známe signatúry súborov: %PDF pre vnorené PDF, hlavičku lokálneho súboru ZIP PK\x03\x04 pre dokumenty Office Open XML, \xD0\xCF\x11\xE0 pre staršie binárne súbory zložených dokumentov. Informácie o type zo slovníka sú vhodné na zobrazenie v popisku používateľského rozhrania, ale nemali by riadiť rozhodnutia o spracovaní, keď máte k dispozícii skutočné bajty.
Odstraňovanie príloh
Metóda DeleteAttachment(Index) odstráni položku na danej pozícii a v prípade úspechu vráti True. Po odstránení sa zostávajúce položky posunú nadol, takže ak odstraňujete viacero príloh v cykle, musíte prechádzať od posledného indexu smerom nadol, nie nahor, aby ste sa vyhli preskočeniu položiek po každom posune. Zmena sa prejaví v súbore až po volaní SaveAs.
Častým scenárom v systémoch spracovania dokumentov je odstránenie všetkých príloh z prichádzajúceho PDF z bezpečnostných dôvodov alebo kvôli veľkosti pred jeho ďalším odoslaním. Pred cyklom zistite počet a potom postupujte v opačnom poradí:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
Kde sa prílohy PDF vyskytujú v praxi
Rozhranie API pre prílohy funguje na akomkoľvek PDF, ktoré dokáže PDFium otvoriť, ale dokumenty, v ktorých sa reálne stretávate s vloženými súbormi, sa sústreďujú okolo niekoľkých špecifických prípadov. Formát PDF/A-3 (ISO 19005-3) explicitne povoľuje vyhovujúce vložené súbory ako mechanizmus na pribalenie zdrojových údajov k archívnemu zobrazeniu; elektronické faktúry ZUGFeRD a Factur-X sa spoliehajú presne na toto, aby vložili štruktúrovaný obsah XML do vizuálneho rozloženia PDF. Súbory PDF vytvorené z e-mailov niekedy prenášajú svoje pôvodné prílohy správ do stromu vložených súborov. Technická dokumentácia, ktorá vznikla v štruktúrovaných autorských systémoch, občas pribaluje podporné materiály rovnakým spôsobom.
Keď vaša aplikácia spracováva prichádzajúce PDF z externého prostredia, kontrola vlastnosti AttachmentCount pri prijímaní dokumentov má význam z dvoch nezávislých dôvodov. Po prvé, vložené súbory môžu niesť údaje, ktoré chcete extrahovať a spracovať, napríklad XML vo fakturačnom PDF. Po druhé, vložené súbory môžu niesť ľubovoľný spustiteľný obsah, takže informácia o ich prítomnosti je dôležitá, aj keď ich nikdy neplánujete extrahovať. Ani jeden z týchto dôvodov od vás nevyžaduje zložité operácie: prečítajte počet, skontrolujte názvy a rozhodnite sa, ako naložíte s bajtmi.
Vlastnosti príloh zobrazené v tomto článku sú súčasťou komponentu PDFium VCL pre Delphi a C++Builder.