A PDF fájlmellékletek a dokumentum beágyazott fájlfájában (embedded-file tree) tárolódnak, amelyet a legtöbb nézőke gemkapocs panelként vagy mellékletek oldalsávként jelenít meg. Delphi kódból a PDFium komponens ezt a fát a TPdf néhány indexelt tulajdonságán keresztül teszi elérhetővé: egész indexek szerint iterálhat, olvashatja a neveket és a bájtokat, új helyeket hozhat létre, és törölheti a meglévőket. Az API felülete szűk; csak néhány sorrendi korlátozást és egy elérési út tisztítási (sanitization) szabályt érdemes ismernie, mielőtt produkciós kódot írna hozzá
Mellékletek olvasása nyitott dokumentumból
A AttachmentCount megadja a dokumentumban deklarált beágyazott fájlok számát. Ezt közvetlenül a PDFium alaphívásából olvassa be, így csak azt tükrözi, amit a PDF valójában tartalmaz. Innen az AttachmentName[Index] visszaadja a megjelenített nevet WString formátumban, az Attachment[Index] pedig a nyers bájtokat TBytes tömbként. Mindkettő nulla-alapú. A dokumentumnak nyitva kell lennie (Pdf.Active = True), mielőtt bármelyik tulajdonságot lekérdezné; lezárt dokumentumon történő hívásuk nullát vagy üres eredményt ad kivétel nélkül
Egy dolgot szem előtt kell tartani: az Attachment[Index] minden olvasáskor lefoglalja és visszaadja a teljes fájlterhelést. Olyan dokumentumnál, amely nagy méretű beágyazott objektumot tartalmaz, a mellékleteken való iterálás a megjelenítési lista felépítéséhez azt jelenti, hogy minden hívásnál megfizeti ezt a lefoglalási költséget. Ha csak a nevekre van szüksége a megjelenítéshez, olvassa be először az AttachmentName tulajdonságot, és halassza el a bájtok lekérését addig, amíg a felhasználó ténylegesen nem kéri a fájlt
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;
Melléklet kicsomagolása a lemezre
Nincs SaveAttachment segédfunkció. A bájtokat beolvassa, és oda írja, ahova szüksége van rájuk, így az elérési út felépítése és tisztítása teljesen az Ön kódjára hárul. Ez különösen akkor számít, ha a mellékletnevek nem megbízható dokumentumokból származnak. A PDF mellékletek nevei a fájlban tárolt karakterláncok; tartalmazhatnak elérésiút-választókat, Unicode-szerű karaktereket és más olyan karaktereket, amelyek váratlan eredményeket hoznak, ha közvetlenül a TFileStream.Create-nek adja át őket. Mindig futtassa át a nevet az ExtractFileName függvényen az output útvonal felépítése előtt, és fontolja meg a ponttal kezdődő vagy a rendszer által nem elvárt karaktereket tartalmazó nevek elutasítását
Az Attachment[Index] by visszaadott bájttömb a hívó tulajdona. Írja ki egy normál TFileStream segítségével, és tetszése szerint kezelheti, beleértve az első néhány bájt megvizsgálását a tényleges fájlformátum ellenőrzéséhez, ahelyett, hogy bízna a deklarált névben
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;
Mellékletek hozzáadása és a kétlépcsős írás
A melléklet létrehozása két hívást igényel, nem egyet. A CreateAttachment(Name) regisztrál egy új helyet a beágyazott fájl fában, és sikeres működés esetén True-t ad vissza. Ez a hely üresen indul. Ezt követően hozzárendeli a tartalmat az Attachment[AttachmentCount - 1] írásával, megcélozva a legutóbb létrehozott bejegyzést. Ha a CreateAttachment False értéket ad vissza, a hely nem jött létre, és a hozzárendelés megrongálná az utolsóként álló mellékletet
A mellékletlista módosítása után a változások csak a memóriában élnek. Hívja meg a SaveAs metódust egy új fájl kiírásához a frissített beágyazott fájl fával. A PDFium komponens jelenleg nem támogatja a megnyitott fájlba történő közvetlen visszamentést, mivel a motor olvasási fogantyút (read handle) tart fenn a forráshoz. A helyben történő frissítés szokásos mintája az, hogy elmenti egy ideiglenes útvonalra, bezárja a dokumentumot, törli vagy átnevezi az eredetit, majd a helyére nevezi át az ideiglenes fájlt, és újra megnyitja
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;
Melléklet típusinformációi
A nézőn és a bájtokon kívül az AttachmentType[Index] visszaadja a PDF beágyazott fájl szótárában tárolt MIME-típus karakterláncot, ha a fájl eredeti csatolásakor rögzítették azt. Sok generáló program üresen hagyja ezt a mezőt, vagy olyan általános értére állítja, mint az application/octet-stream, így egy éles feldolgozóban nem hagyatkozhat rá a formátum észleléséhez. A megbízható azonosításhoz olvassa be a tartalom első néhány bájtját, és ellenőrizze az ismert fájl-aláírásokat (file signatures): %PDF beágyazott PDF-hez, a ZIP helyi fájlfejléce PK\x03\x04 az Office Open XML dokumentumokhoz, és \xD0\xCF\x11\xE0 a régi compound-file binárisokhoz. A szótárból származó típusinformáció jó a felhasználói felületen való megjelenítésre, de nem szabad, hogy feldolgozási döntéseket vezéreljen, amikor a tényleges bájtok is rendelkezésre állnak
Mellékletek törlése
A DeleteAttachment(Index) eltávolítja a bejegyzést az adott pozícióból, és sikeres működés esetén True-t ad vissza. Törlés után a megmaradt bejegyzések lefelé tolódnak el, így ha egy ciklusban több mellékletet töröl, az utolsó indextől lefelé kell haladnia, nem pedig előre, hogy elkerülje a bejegyzések kihagyását az eltolódás után. A változás a memóriában marad a SaveAs meghívásáig
A dokumentumfeldolgozási folyamatok gyakori forgatókönyve az összes melléklet eltávolítása a beérkező PDF-ből biztonsági vagy méretbeli okokból, mielőtt azt továbbítanák. Számolja meg őket egyszer a ciklus előtt, és iteráljon visszafelé:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
Hol fordulnak elő PDF mellékletek a gyakorlatban
A melléklet API minden olyan PDF-en működik, amelyet a PDFium meg tud nyitni, de azok a dokumentumok, amelyekben ténylegesen találkozhat beágyazott fájlokkal, néhány konkrét esetre korlátozódnak. A PDF/A-3 (ISO 19005-3) kifejezetten engedélyezi a megfelelő beágyazott fájlok használatát forrásadatok kötegelésére az archivált változat mellett; a ZUGFeRD és Factur-X elektronikus számlák pontosan erre támaszkodnak a strukturált XML tartalom beágyazásához a humán-olvasható PDF elrendezésbe. Az e-mailből származó PDF-ek néha az eredeti üzenetmellékleteket hordozzák továbbítva a beágyazott fájlok fájában. A strukturált szerzői rendszerekből származó műszaki dokumentációk alkalmanként ugyanígy kötegelik a támogató elemeket
Ha az alkalmazása a szervezeten kívülről érkező PDF-eket dolgoz fel, az AttachmentCount ellenőrzését érdemes elvégezni a dokumentum befogadásakor két független okból is. Először is, a beágyazott fájlok olyan adatokat tartalmazhatnak, amelyeket ki szeretne nyerni és fel szeretne dolgozni, például egy számla PDF-ben lévő XML-t. Másodszor, a beágyazott fájlok tetszőleges futtatható tartalmat hordozhatnak, így annak ismerete, hogy mi van jelen, akkor is számít, ha soha nem áll szándékában kinyerni azt. Egyik ok sem követel meg bonyolult dolgokat: olvassa le a darabszámot, ellenőrizze a neveket, és döntse el, mit tesz a bájtokgal
Az itt bemutatott melléklet-tulajdonságok a Delphihez és C++Builderhez készült PDFium komponens részét képezik