PDF-Dateianhänge werden im Baum für eingebettete Dateien des Dokuments gespeichert – einer Struktur, die die meisten Viewer als Büroklammer-Symbolleiste oder Seitenleiste für Anhänge anzeigen. Im Delphi-Code legt die PDFium-Komponente diesen Baum über eine kleine Gruppe indizierter Eigenschaften auf TPdf offen: Sie durchlaufen die Einträge per ganzzahligem Index, lesen Namen und Byte-Payloads aus, erstellen neue Plätze und löschen vorhandene. Die API-Oberfläche ist schmal; es gibt nur wenige Reihenfolgebeschränkungen und eine Pfadbereinigungsregel, die man kennen sollte, bevor man produktiven Code darum herum schreibt
Auslesen von Anhängen aus einem geöffneten Dokument
AttachmentCount liefert die automatisch ermittelte Anzahl der eingebetteten Dateien, die das Dokument deklariert. Es liest direkt aus dem darunterliegenden PDFium-Aufruf, spiegelt also nur das wider, was das PDF tatsächlich enthält. Von dort aus gibt AttachmentName[Index] den Anzeigenamen als WString zurück, und Attachment[Index] liefert die rohen Bytes als TBytes-Array. Beide sind nullbasiert. Das Dokument muss geöffnet sein (Pdf.Active = True), bevor Sie eine der beiden Eigenschaften abfragen. Der Aufruf bei einem geschlossenen Dokument liefert null oder ein leeres Ergebnis ohne Exception
Eines sollte man beachten: Attachment[Index] reserviert und liefert bei jedem Lesezugriff die vollständige Datei-Payload. Bei einem Dokument, das eine große eingebettete Datei enthält, bedeutet das Durchlaufen aller Anhänge zum Aufbau einer Anzeigeliste, dass diese Reservierungskosten bei jedem Aufruf anfallen. Wenn Sie nur die Namen für Anzeigezwecke benötigen, lesen Sie zuerst AttachmentName und verschieben Sie den Abruf der Bytes, bis der Benutzer die Datei tatsächlich anfordert
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;
Extrahieren eines Anhangs auf die Festplatte
Es gibt keine Hilfsfunktion wie SaveAttachment. Sie lesen die Bytes aus und schreiben sie dorthin, wo Sie sie benötigen, wodurch die Pfaderstellung und -bereinigung vollständig in der Verantwortung Ihres Codes liegt. Das ist wichtig, wenn die Namen der Anhänge aus nicht vertrauenswürdigen Dokumenten stammen. Die Namen von PDF-Anhängen sind im Dokument gespeicherte Zeichenfolgen. Sie können Pfadtrennzeichen, Unicode-Lookalikes und andere Zeichen enthalten, die zu unerwarteten Ergebnissen führen, wenn Sie sie direkt an TFileStream.Create übergeben. Führen Sie den Namen immer durch ExtractFileName, bevor Sie einen Ausgabepfad erstellen, und erwägen Sie, Namen abzulehnen, die mit einem Punkt beginnen oder Zeichen außerhalb der von Ihrem System erwarteten Zeichen enthalten
Das von Attachment[Index] zurückgegebene Byte-Array gehört dem Aufrufer. Schreiben Sie es mit einem normalen TFileStream aus, und Sie können damit verfahren, wie Sie möchten – einschließlich der Überprüfung der ersten Bytes, um das tatsächliche Dateiformat zu verifizieren, anstatt dem deklarierten Namen zu vertrauen
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;
Hinzufügen von Anhängen und das Schreiben in zwei Schritten
Das Erstellen eines Anhangs erfordert zwei Aufrufe, nicht einen. CreateAttachment(Name) registriert einen neuen Platz im Baum der eingebetteten Dateien und gibt bei Erfolg True zurück. Dieser Platz ist anfangs leer. Anschließend weisen Sie die Payload zu, indem Sie in Attachment[AttachmentCount - 1] schreiben und damit den zuletzt erstellten Eintrag anvisieren. Wenn CreateAttachment den Wert False zurückgibt, wurde der Platz nicht erstellt, und die Zuweisung würde den Anhang am Index beschädigen, der zufällig der letzte ist
Nach dem Ändern der Anhangsliste verbleiben die Änderungen nur im Speicher. Rufen Sie SaveAs auf, um eine neue Datei mit dem aktualisierten Baum der eingebetteten Dateien zu schreiben. Die PDFium-Komponente unterstützt derzeit kein Speichern in dieselbe Datei, die gerade geöffnet ist, da die Engine ein Lesehandle auf die Quelle hält. Das Standardverfahren für ein In-Place-Update besteht darin, in einen temporären Pfad zu speichern, das Dokument zu schließen, das Original zu löschen oder umzubenennen, dann die temporäre Datei an die richtige Stelle umzubenennen und erneut zu öffnen
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;
Typinformationen von Anhängen
Zusätzlich zum Namen und der Byte-Payload gibt AttachmentType[Index] die im Embedded-File-Dictionary des PDFs gespeicherte MIME-Typ-Zeichenfolge zurück, falls eine beim ursprünglichen Anhängen der Datei aufgezeichnet wurde. Viele Generatoren lassen dieses Feld leer oder setzen es auf einen generischen Wert wie application/octet-stream, sodass Sie sich in einer Produktionspipeline nicht auf dieses Feld zur Formaterkennung verlassen können. Für eine zuverlässige Identifizierung lesen Sie die ersten Bytes der Payload und prüfen Sie auf bekannte Dateisignaturen: %PDF für ein verschachteltes PDF, den lokalen ZIP-Dateikopf PK\x03\x04 für Office Open XML-Dokumente, \xD0\xCF\x11\xE0 für ältere Compound-File-Binärdateien. Typinformationen aus dem Dictionary sind in Ordnung, um sie in einer UI-Beschriftung anzuzeigen, sollten jedoch keine Verarbeitungsentscheidungen steuern, wenn Ihnen die tatsächlichen Bytes zur Verfügung stehen
Löschen von Anhängen
DeleteAttachment(Index) entfernt den Eintrag an dieser Position und gibt bei Erfolg True zurück. Nach dem Löschen verschieben sich die verbleibenden Einträge nach unten. Wenn Sie also mehrere Anhänge in einer Schleife löschen, müssen Sie vom letzten Index abwärts und nicht vorwärts iterieren, um das Überspringen von Einträgen nach jedem Verschiebevorgang zu vermeiden. Die Änderung verbleibt im Speicher, bis Sie SaveAs aufrufen
Ein häufiges Szenario in Dokumentenverarbeitungs-Pipelines besteht darin, alle Anhänge aus einer eingehenden PDF-Datei aus Sicherheits- oder Größengründen zu entfernen, bevor sie weitergeleitet wird. Zählen Sie vor der Schleife einmal und iterieren Sie rückwärts:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
Wo PDF-Anhänge in der Praxis vorkommen
Die Anhangs-API funktioniert bei jedem PDF, das PDFium öffnen kann. Die Dokumente, in denen Sie tatsächlich eingebetteten Dateien begegnen, konzentrieren sich jedoch auf einige wenige spezifische Fälle. PDF/A-3 (ISO 19005-3) erlaubt konforme eingebettete Dateien explizit als Mechanismus zum Bündeln von Quelldaten neben der Archivversion; elektronische Rechnungen im ZUGFeRD- und Factur-X-Format stützen sich auf genau diesen Mechanismus, um eine strukturierte XML-Payload in das für Menschen lesbare PDF-Layout einzubetten. Aus E-Mails generierte PDFs enthalten manchmal ihre ursprünglichen Nachrichtenanhänge, die in den Baum der eingebetteten Dateien weitergeleitet wurden. Technische Dokumentationen, die aus strukturierten Redaktionssystemen stammen, bündeln unterstützende Assets gelegentlich auf dieselbe Weise
Wenn Ihre Anwendung eingehende PDFs von außerhalb Ihrer Organisation verarbeitet, lohnt es sich, AttachmentCount im Rahmen der Dokumentenaufnahme aus zwei unabhängigen Gründen zu prüfen. Erstens können eingebettete Dateien Daten enthalten, die Sie extrahieren und verarbeiten möchten, wie z. B. das XML in einer Rechnungs-PDF. Zweitens können eingebettete Dateien beliebige ausführbare Inhalte enthalten, sodass es wichtig ist zu wissen, was vorhanden ist, selbst wenn Sie niemals beabsichtigen, es zu extrahieren. Keiner der beiden Gründe erfordert komplexe Maßnahmen: Lesen Sie die Anzahl, prüfen Sie die Namen und entscheiden Sie, was mit den Bytes geschehen soll
Die hier gezeigten Eigenschaften für Anhänge sind Teil der PDFium-Komponente für Delphi und C++Builder