PDF failų priedai (attachments) yra saugomi dokumento įterptųjų failų medyje (embedded-file tree) – struktūroje, kurią dauguma peržiūros programų atvaizduoja kaip sąvaržėlės skydelį arba priedų šoninę juostą. „Delphi“ kode „PDFium VCL“ atveria šį medį per nedidelį indeksuotų savybių rinkinį TPdf komponente: galite atlikti iteraciją pagal sveikojo skaičiaus indeksą, skaityti pavadinimus ir baitų duomenis (payloads), sukurti naujas vietas (slots) ir ištrinti esamas. API yra nedidelis – tereikia žinoti keletą eiliškumo apribojimų ir vieną duomenų valymo (sanitization) taisyklę prieš pradedant rašyti gamybinį kodą.
Priedų skaitymas iš atidaryto dokumento
Savybė AttachmentCount nurodo įterptųjų failų skaičių, kurį deklaruoja dokumentas. Ji skaitoma tiesiogiai iš vidinio PDFium iškvietimo, todėl atspindi tik tai, kas iš tikrųjų yra PDF faile. Toliau AttachmentName[Index] grąžina rodomą pavadinimą kaip WString, o Attachment[Index] pateikia neapdorotus baitus kaip TBytes masyvą. Abu indeksuojami nuo nulio. Dokumentas privalo būti atidarytas (Pdf.Active := True) prieš užklausiant bet kurią iš šių savybių; bandymas jas pasiekti uždarytame dokumente grąžins nulį arba tuščią rezultatą be jokios išimties.
Vienas svarbus aspektas: Attachment[Index] kiekvieno skaitymo metu išskiria atmintį ir grąžina visus failo duomenis. Jei dokumente yra didelių įterptųjų failų, iteracija per visus priedus vien tam, kad sukurtumėte rodomą sąrašą, reikš atminties išskyrimo sąnaudas kiekvieno iškvietimo metu. Jei pavadinimų reikia tik atvaizdavimui, pirmiausia perskaitykite AttachmentName, o baitų gavimą atidėkite iki to momento, kai naudotojas iš tikrųjų paprašys failo.
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;
Priedo išgavimas į diską
Nėra jokios pagalbinės funkcijos SaveAttachment. Jūs patys perskaitote baitus ir įrašote juos ten, kur reikia, todėl kelio kūrimas ir saugumo patikra visiškai prikaiuso nuo jūsų kodo. Tai ypač svarbu, kai priedų pavadinimai gaunami iš nepatikimų dokumentų. PDF priedų pavadinimai yra faile saugomos eilutės; juose gali būti kelių skyriklių, panašiai atrodančių „Unicode“ simbolių ar kitų ženklų, kurie sukels netikėtų rezultatų, jei juos tiesiogiai perduosite funkcijai TFileStream.Create. Prieš kurdami bet kokį išvesties kelią, visada apdorokite pavadinimą naudodami ExtractFileName, taip pat apsvarstykite galimybę atmesti pavadinimus, kurie prasideda tašku arba turi jūsų sistemoje netikėtų simbolių.
Baitų masyvas, kurį grąžina Attachment[Index], priklauso kviečiančiajai programai. Įrašykite jį naudodami įprastą TFileStream ir galėsite su juo daryti ką norite, įskaitant pirmųjų baitų peržiūrą, kad patikrintumėte tikrąjį failo formatą, užuot pasitikėję deklaruotu pavadinimu.
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;
Priedų pridėjimas ir dviejų žingsnių įrašymas
Priedo sukūrimas reikalauja dviejų iškvietimų, o ne vieno. Metodas CreateAttachment(Name) užregistruoja naują vietą (slot) įterptųjų failų medyje ir grąžina True sėkmės atveju. Ši vieta pradžioje būna tuščia. Tuomet priskiriate duomenis rašydami į Attachment[AttachmentCount - 1] – taip nusitaikoma į paskutinį sukurtą įrašą. Jei CreateAttachment grąžina False, vieta nebuvo sukurta, o priskyrimas sugadintų priedą, esantį paskutiniame indekse.
Pakeitus priedų sąrašą, visi pakeitimai lieka tik atmintyje. Iškvieskite SaveAs, kad įrašytumėte naują failą su atnaujintu įterptųjų failų medžiu. „PDFium VCL“ šiuo metu nepalaiko įrašymo į tą patį atidarytą failą, nes variklis laiko atidarytą šaltinio skaitymo rankeną (read handle). Įprastas tiesioginio atnaujinimo (in-place update) šablonas yra įrašymas laikinajame kelyje, dokumento uždarymas, originalo ištrynimas arba perveidinimas, o tuomet laikinojo failo pervadinimas į originalų ir pakartotinis jo atidarymas.
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;
Priedo tipo informacija
Be pavadinimo ir baitų duomenų, AttachmentType[Index] grąžina MIME tipo eilutę, saugomą PDF įterptojo failo žodyne, jei ji buvo įrašyta pridedant failą. Daugelis generatorių palieka šį lauką tuščią arba nustato bendrinę reikšmę, pavyzdžiui, application/octet-stream, todėl gamybos konvejeryje negalite ja pasikliauti formato nustatymui. Patikimam atpažinimui perskaitykite pirmuosius duomenų baitus ir patikrinkite žinomus failų parašus: %PDF įterptam PDF failui, ZIP vietinio failo antraštę PK\x03\x04 „Office Open XML“ dokumentams, \xD0\xCF\x11\xE0 senesniems sudėtiniams dvejetainiams failams. Tipo informacija iš žodyno puikiai tinka rodyti vartotojo sąsajoje, tačiau neturėtų lemti apdorojimo sprendimų, kai turite prieinamus tikruosius baitus.
Priedų trynimas
Metodas DeleteAttachment(Index) pašalina įrašą toje pozicijoje ir sėkmės atveju grąžina True. Po ištrynimo likę įrašai pasislenka žemyn, todėl, jei trinate kelis priedus cikle, privalote atlikti iteraciją atgaline tvarka (nuo paskutinio indekso iki pirmo), o ne į priekį, kad išvengtumėte įrašų praleidimo po kiekvieno pasislinkimo. Pakeitimas lieka atmintyje, kol iškviesite SaveAs.
Dažnas scenarijus dokumentų apdorojimo konvejeriuose yra visų priedų pašalinimas iš gaunamo PDF failo prieš perduodant jį tolesniam apdorojimui dėl saugumo ar failo dydžio sumetimų. Suskaičiuokite priedus vieną kartą prieš ciklą ir vykdykite iteraciją atbuline eiga:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
Kur PDF priedai sutinkami praktikoje
Priedų API veikia su bet kokiu PDF failu, kurį gali atidaryti PDFium, tačiau dokumentai, kuriuose praktiškai sutinkami įterptieji failai, paprastai priskiriami keliems konkretiems atvejams. PDF/A-3 (ISO 19005-3) aiškiai leidžia įterpti reikalavimus atitinkančius failus kaip mechanizmą šaltinio duomenims saugoti kartu su archyviniu variantu; „ZUGFeRD“ ir „Factur-X“ elektroninės sąskaitos-faktūros remiasi būtent tuo, kad įterptų struktūrizuotą XML dokumentą į žmonėms skaitomą PDF maketą. Iš el. laiškų gauti PDF failai kartais turi pradinių laiškų priedus, perkeltus į įterptųjų failų medį. Techninė dokumentacija, sukurta struktūrizuotose autorystės sistemose, kartais tokiu pat būdu talpina susijusius išteklius.
Kai jūsų programa apdoroja gaunamus PDF failus iš išorinių organizacijų, atlikti patikrą su AttachmentCount dokumentų priėmimo metu verta dėl dviejų nepriklausomų priežasčių. Pirma, įterptieji failai gali turėti duomenų, kuriuos norite išgauti ir apdoroti, pavyzdžiui, XML sąskaitos-faktūros PDF faile. Antra, įterptuose failuose gali būti bet kokio vykdomojo turinio, todėl svarbu žinoti, kas yra viduje, net jei neketinate to išgauti. Nė viena priežastis nereikalauja sudėtingų veiksmų: perskaitykite skaičių, patikrinkite pavadinimus ir nuspręskite, ką daryti su baitais.
Šiame straipsnyje parodytos priedų savybės yra „Delphi“ ir „C++Builder“ skirtos bibliotekos PDFium VCL komponento dalis.