PDF-liitetiedostot tallennetaan asiakirjan upotettujen tiedostojen puuhun (embedded-file tree), rakenteeseen, jonka useimmat katseluohjelmat tuovat näkyviin klemmaripaneelina tai liitesivupalkkina. Delphin koodista käsin PDFium-komponentti tuo tämän puun näkyville TPdf-luokan pienen indeksoitujen propertyjen joukon kautta: käydään läpi kokonaislukuindeksejä, luetaan nimiä ja tavukuormia (payloads), luodaan uusia paikkoja ja poistetaan olemassa olevia. API-rajapinta on kapea; siinä on vain muutama järjestyssääntö ja yksi polun puhdistussääntö, jotka on hyvä tietää ennen tuotantokoodin kirjoittamista
Liitetiedostojen lukeminen avoimesta asiakirjasta
AttachmentCount antaa asiakirjan ilmoittamien upotettujen tiedostojen määrän. Se lukee arvon suoraan PDFiumin taustalla olevasta kutsusta, joten se heijastaa vain sitä, mitä PDF todellisuudessa sisältää. Siitä eteenpäin AttachmentName[Index] palauttaa näyttönimen WString-tyyppinä, ja Attachment[Index] palauttaa raa'at tavut TBytes-taulukkona. Molemmat ovat nollapohjaisia. Asiakirjan on oltava auki (Pdf.Active = True) ennen kummankaan propertyn kyselyä; niiden kutsuminen suljetulla asiakirjalla palauttaa nollan tai tyhjän tuloksen ilman poikkeusta
Yksi asia on hyvä pitää mielessä: Attachment[Index] varaa muistia ja palauttaa koko tiedoston sisällön jokaisella lukukerralla. Jos asiakirjassa on suuri upotettu tiedosto, kaikkien liitteiden läpikäynti näyttöluettelon rakentamiseksi tarkoittaa tämän muistinvaraustoimenpiteen tekemistä jokaisessa kutsussa. Jos tarvitset vain nimiä näyttötarkoituksiin, lue ensin AttachmentName ja lykkää tavujen hakua siihen asti, kunnes käyttäjä todella pyytää tiedostoa
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;
Liitteen purkaminen levylle
Erillistä SaveAttachment-apuohjelmaa ei ole. Luet tavut ja kirjoitat ne sinne missä niitä tarvitset, mikä jättää polun rakentamisen ja puhdistamisen kokonaan oman koodisi vastuulle. Tällä on merkitystä silloin, kun liitteiden nimet ovat peräisin epäluotettavista asiakirjoista. PDF-liitteiden nimet ovat tiedoston sisään tallennettuja merkkijonoja; ne voivat sisältää polun erottimia, Unicode-kaksoiskappaleita ja muita merkkejä, jotka tuottavat odottamattomia tuloksia, jos välität ne suoraan TFileStream.Create-kutsulle. Aja nimi aina ExtractFileName-funktion läpi ennen minkään tulostuspolun rakentamista, ja harkitse pisteellä alkavien tai järjestelmäsi odotusten ulkopuolisia merkkejä sisältävien nimien hylkäämistä
Metodin Attachment[Index] palauttaman tavutaulukon omistaa sen kutsuja. Kirjoita se ulos tavallisella TFileStream-oliolla, ja voit käsitellä sitä haluamallasi tavalla, mukaan lukien ensimmäisten tavujen tarkistaminen todellisen tiedostomuodon varmistamiseksi sen sijaan, että luottaisit ilmoitettuun nimeen
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;
Liitteiden lisääminen ja kaksivaiheinen kirjoitus
Liitteen luominen vaatii kaksi kutsua, ei yhtä. CreateAttachment(Name) rekisteröi uuden paikan upotettujen tiedostojen puuhun ja palauttaa arvon True onnistuessaan. Tämä paikka alkaa tyhjänä. Sen jälkeen sijoitat tiedoston sisällön kirjoittamalla Attachment[AttachmentCount - 1]-paikkaan, kohdistaen viimeisimpänä luotuun merkintään. Jos CreateAttachment palauttaa False, paikkaa ei luotu ja sijoitus korruptoisi liitteen siinä indeksissä, joka sattuu olemaan viimeisenä
Liitelistan muuttamisen jälkeen muutokset elävät vain muistissa. Kutsu SaveAs-metodia kirjoittaaksesi uuden tiedoston päivitetyn upotetun tiedostopuun kanssa. PDFium-komponentti ei tue tallentamista takaisin samaan tiedostoon, joka on parhaillaan auki, koska moottorilla on lukukahva lähteeseen. Vakioratkaisu paikan päällä tehtävään päivitykseen on tallentaa tilapäiseen polkuun, sulkea asiakirja, poistaa tai nimetä uudelleen alkuperäinen tiedosto ja sen jälkeen siirtää tilapäistiedosto paikalleen ja avata se uudelleen
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;
Liitteen tyyppitiedot
Nimen ja tavukuorman lisäksi AttachmentType[Index] palauttaa PDF:n upotetun tiedoston sanakirjaan tallennetun MIME-tyyppimerkkijonon, jos sellainen kirjattiin silloin, kun tiedosto alun perin liitettiin. Monet luontiohjelmat jättävät tämän kentän tyhjäksi tai asettavat sille yleisen arvon kuten application/octet-stream, joten et voi luottaa siihen muodon tunnistamisessa tuotantoputkessa. Luotettavaa tunnistusta varten lue tavujen ensimmäiset tavut ja tarkista tunnetut tiedostoallekirjoitukset: %PDF sisäkkäiselle PDF:lle, ZIP-paikallinen tiedosto-otsikko PK\x03\x04 Office Open XML -asiakirjoille, \xD0\xCF\x11\xE0 vanhoille yhdistelmätiedostobinaareille (legacy compound-file binaries). Sanakirjasta saatu tyyppitieto on hienoa näyttää käyttöliittymän etiketissä, mutta sen ei tulisi ohjata käsittelypäätöksiä, kun sinulla on todelliset tavut käytettävissä
Liitetiedostojen poistaminen
DeleteAttachment(Index) poistaa merkinnän kyseisestä kohdasta ja palauttaa arvon True onnistuessaan. Poistamisen jälkeen loput merkinnät siirtyvät alaspäin, joten jos poistat useita liitteitä silmukassa, sinun on käytävä ne läpi viimeisestä indeksistä alaspäin (eikä eteenpäin), jotta vältät merkintöjen hyppäämisen kunkin siirtymän jälkeen. Muutos on muistissa, kunnes kutsut SaveAs-metodia
Yleinen tilanne asiakirjojen käsittelyputkissa on kaikkien liitteiden poistaminen saapuvasta PDF-tiedostosta ennen sen lähettämistä eteenpäin tietoturva- tai kokosyistä. Laske määrä kerran ennen silmukkaa ja käy läpi käänteisessä järjestyksessä:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
Missä PDF-liitetiedostoja esiintyy käytännössä
Liite-API toimii millä tahansa PDF-tiedostolla, jonka PDFium voi avata, mutta asiakirjat, joissa todella kohtaat upotettuja tiedostoja, keskittyvät muutamaan erityistapaukseen. PDF/A-3 (ISO 19005-3) nimenomaisesti sallii standardinmukaisten upotettujen tiedostojen käytön mekanismina lähdetiedon nippuamiseksi arkistoitavan esityksen rinnalle; ZUGFeRD- ja Factur-X-sähköiset laskut tukeutuvat juuri tähän upottaessaan rakenteellisen XML-sisällön ihmisluettavan PDF-asettelun sisälle. Sähköposteista luodut PDF-tiedostot kantavat joskus alkuperäisiä viestiliitteitään upotettujen tiedostojen puuhun siirrettyinä. Rakennetuissa kirjoitusjärjestelmissä (structured authoring systems) luodut tekniset dokumentaatiot niputtavat toisinaan tukiresursseja samalla tavalla
Kun sovelluksesi käsittelee organisaatiosi ulkopuolelta tulevia PDF-tiedostoja, AttachmentCount-arvon tarkistaminen osana asiakirjan vastaanottoa on suositeltavaa kahdesta itsenäisestä syystä. Ensinnäkin upotetut tiedostot voivat kantaa tietoa, jonka haluat poimia ja käsitellä, kuten XML-tieto lasku-PDF:n sisällä. Toiseksi upotetut tiedostot voivat sisältää mielivaltaista suoritettavaa sisältöä, joten tietoisuus niiden läsnäolosta on tärkeää, vaikka et koskaan aikoisi purkaa niitä. Kumpikaan syy ei vaadi mitään monimutkaista: lue määrä, tarkista nimet ja päätä, mitä teet tavuille
Tässä esitetyt liite-propertyt ovat osa Delphille ja C++Builderille tarkoitettua PDFium-komponenttia