PDF prilozi (attachments) se čuvaju u stablu ugrađenih datoteka u dokumentu, što je struktura koju većina čitača prikazuje kao panel sa spajalicom ili bočnu traku sa prilozima. Iz Delphi koda, PDFium VCL izlaže to stablo kroz mali skup indeksiranih svojstava na klasi TPdf: prolazite kroz njih pomoću celobrojnog indeksa, čitate nazive i nizove bajtova, kreirate nova mesta i brišete postojeća. API interfejs je jednostavan; postoji samo nekoliko ograničenja u redosledu i jedno pravilo sanitizacije koje vredi znati pre nego što napišete produkcioni kod.
Čitanje priloga iz otvorenog dokumenta
Svojstvo AttachmentCount daje broj ugrađenih datoteka koje dokument deklariše. Ono čita vrednost direktno iz osnovnog PDFium poziva, tako da odražava samo ono što PDF zaista sadrži. Odatle, AttachmentName[Index] vraća ime za prikaz kao WString, a Attachment[Index] vraća sirove bajtove kao TBytes niz. Oba indeksa su zasnovana na 0. Dokument mora biti otvoren (Pdf.Active = True) pre nego što uputite upit za bilo koje od ovih svojstava; pozivanje nad zatvorenim dokumentom vraća nulu ili prazan rezultat bez podizanja izuzetka.
Jedna stvar koju treba imati na umu: Attachment[Index] alocira i vraća kompletan sadržaj datoteke pri svakom čitanju. Za dokument koji nosi veliku ugrađenu datoteku, prolazak kroz sve priloge radi pravljenja liste za prikaz znači plaćanje tog troška alokacije pri svakom pozivu. Ako su vam potrebna samo imena za prikaz, prvo pročitajte AttachmentName, a preuzimanje bajtova odložite dok korisnik stvarno ne zatraži datoteku.
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;
Ekstrakcija priloga na disk
Ne postoji pomoćna funkcija SaveAttachment. Vi čitate bajtove i upisujete ih tamo gde vam je potrebno, što konstrukciju i sanitizaciju putanje prepušta u potpunosti vašem kodu. To je važno kada nazivi priloga dolaze iz nepouzdanih dokumenata. Nazivi PDF priloga su stringovi sačuvani unutar datoteke; mogu sadržati separatore putanja, Unicode vizuelno slične znakove i druge karaktere koji će proizvesti neočekivane rezultate ako ih direktno prosledite u TFileStream.Create. Uvek propustite ime kroz ExtractFileName pre nego što napravite izlaznu putanju i razmislite o odbijanju imena koja počinju tačkom ili sadrže znakove van očekivanih za vaš sistem.
Niz bajtova koji vraća Attachment[Index] je u vlasništvu pozivaoca. Upišite ga pomoću standardnog TFileStream-a i možete raditi sa njim šta god želite, uključujući proveru prvih nekoliko bajtova radi verifikacije stvarnog formata datoteke umesto da verujete deklarisanom imenu.
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;
Dodavanje priloga i upis u dva koraka
Kreiranje priloga zahteva dva poziva, a ne jedan. Metoda CreateAttachment(Name) registruje novo mesto u stablu ugrađenih datoteka i vraća True u slučaju uspeha. To mesto je u početku prazno. Zatim dodeljujete sadržaj upisom u Attachment[AttachmentCount - 1], ciljajući poslednji kreirani unos. Ako CreateAttachment vrati False, mesto nije kreirano i dodeljivanje bi oštetilo prilog na indeksu koji je trenutno poslednji.
Nakon izmene liste priloga, promene postoje samo u memoriji. Pozovite SaveAs da biste upisali novu datoteku sa ažuriranim stablom ugrađenih datoteka. PDFium VCL trenutno ne podržava čuvanje nazad u istu datoteku koja je otvorena, jer motor drži otvoren opisnik (handle) za čitanje izvora. Standardni šablon za izmenu u mestu (in-place) je čuvanje na privremenu putanju, zatvaranje dokumenta, brisanje ili preimenovanje originala, a zatim preimenovanje privremene datoteke na pravo mesto i njeno ponovno otvaranje.
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;
Informacije o tipu priloga
Pored imena i niza bajtova, AttachmentType[Index] vraća MIME tip sačuvan u rečniku ugrađene datoteke u PDF-u, ako je bio zabeležen kada je datoteka prvobitno priložena. Mnogi generatori ostavljaju ovo polje praznim ili ga postavljaju na generičku vrednost poput application/octet-stream, tako da se ne možete osloniti na njega za detekciju formata u produkcijskom sistemu. Za pouzdanu identifikaciju, pročitajte prvih nekoliko bajtova sadržaja i proverite poznate potpise datoteka: %PDF za ugnježdeni PDF, zaglavlje lokalne ZIP datoteke PK\x03\x04 za Office Open XML dokumente, \xD0\xCF\x11\xE0 za stare binarne formate. Informacije o tipu iz rečnika su pogodne za prikazivanje na korisničkom interfejsu, ali ne bi trebalo da donose odluke o obradi kada su vam na raspolaganju stvarni bajtovi.
Brisanje priloga
Metoda DeleteAttachment(Index) uklanja unos na toj poziciji i vraća True u slučaju uspeha. Nakon brisanja, preostali unosi se pomeraju naniže, pa ako brišete više priloga u petlji, morate ići unazad od poslednjeg indeksa, a ne unapred, kako biste izbegli preskakanje unosa nakon svakog pomeranja. Promena ostaje samo u memoriji dok ne pozovete SaveAs.
Uobičajeni scenario u sistemima za obradu dokumenata jeste uklanjanje svih priloga iz dolaznog PDF-a pre slanja dalje, iz bezbednosnih razloga ili radi smanjenja veličine. Izbrojte unose jednom pre petlje i prođite kroz njih unazad:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
Gde se PDF prilozi pojavljuju u praksi
API za priloge radi na bilo kom PDF-u koji PDFium može da otvori, ali se dokumenti u kojima stvarno nailazite na ugrađene datoteke grupišu oko nekoliko specifičnih slučajeva. PDF/A-3 (ISO 19005-3) elektronski dozvoljava ugrađene datoteke kao mehanizam za pakovanje izvornih podataka uz arhivski prikaz; elektronski računi ZUGFeRD i Factur-X se oslanjaju upravo na to da bi ugradili strukturirani XML sadržaj unutar čitljivog PDF rasporeda. PDF dokumenti dobijeni iz e-pošte ponekad nose svoje originalne priloge poruka prosleđene u stablo ugrađenih datoteka. Tehnička dokumentacija nastala u sistemima za strukturirano autorstvo povremeno na isti način pakuje prateće resurse.
Kada vaša aplikacija obrađuje dolazne PDF dokumente izvan vaše organizacije, proveru AttachmentCount u sklopu prijema dokumenta vredi uraditi iz dva nezavisna razloga. Prvo, ugrađene datoteke mogu nositi podatke koje želite da izdvojite i obradite, kao što je XML unutar PDF računa. Drugo, ugrađene datoteke mogu nositi proizvoljan izvršni sadržaj, pa je poznavanje onoga što je prisutno važno čak i kada ne nameravate da ih raspakujete. Nijedan od ovih razloga ne zahteva komplikovane akcije: pročitajte broj, proverite imena i odlučite šta ćete raditi sa bajtovima.
Svojstva priloga prikazana ovde su deo komponente PDFium VCL component za Delphi i C++Builder.