Technical Article

Werken met PDF-bijlagen in Delphi met de PDFium-component

· PDF-programmeren

Leer hoe u met PDF-bijlagen werkt in Delphi met de PDFium-component.

Dit artikel is bedoeld voor ontwikkelaars die met pdf-programmeren werken. Productnamen, API-namen, bestandsnamen en codefragmenten zijn bewust ongewijzigd gehouden, zodat de voorbeelden direct met de oorspronkelijke documentatie en broncode te vergelijken zijn.

Overzicht

De pagina beschrijft het probleemgebied, de relevante implementatiekeuzes en de controles die belangrijk zijn voordat de oplossing in een echte toepassing wordt gebruikt.

Codevoorbeeld

Het onderstaande codefragment is ongewijzigd uit de Engelse bron overgenomen om identifiers, API-aanroepen en syntaxis exact te behouden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
procedure TFormMain.RefreshAttachmentList;
var
  I: Integer;
  AttachmentName: string;
  AttachmentData: TBytes;
  SizeStr: string;
begin
  ListBoxAttachments.Items.Clear;
  
  if not Pdf.Active then
    Exit;
    
  for I := 0 to Pdf.AttachmentCount - 1 do
  begin
    AttachmentName := Pdf.AttachmentName[I];
    AttachmentData := Pdf.Attachment[I];
    
    // Format size
    if Length(AttachmentData) >= 1024 * 1024 then
      SizeStr := Format('%.2f MB', [Length(AttachmentData) / (1024 * 1024)])
    else if Length(AttachmentData) >= 1024 then
      SizeStr := Format('%.2f KB', [Length(AttachmentData) / 1024])
    else
      SizeStr := Format('%d bytes', [Length(AttachmentData)]);
      
    ListBoxAttachments.Items.Add(Format('%s (%s)', [AttachmentName, SizeStr]));
  end;
  
  LogStatus(Format('Found %d attachment(s)', [Pdf.AttachmentCount]));
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
procedure TFormMain.ButtonExtractAttachmentClick(Sender: TObject);
var
  Index: Integer;
  AttachmentName: string;
  AttachmentData: TBytes;
  FileStream: TFileStream;
begin
  Index := GetSelectedAttachmentIndex;
  if Index < 0 then
  begin
    LogStatus('Please select an attachment to extract.');
    Exit;
  end;
  
  try
    AttachmentName := Pdf.AttachmentName[Index];
    AttachmentData := Pdf.Attachment[Index];
    
    SaveDialog.FileName := AttachmentName;
    if SaveDialog.Execute then
    begin
      FileStream := TFileStream.Create(SaveDialog.FileName, fmCreate);
      try
        if Length(AttachmentData) > 0 then
          FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
          
        LogStatus('Extracted: ' + SaveDialog.FileName);
      finally
        FileStream.Free;
      end;
    end;
    
  except
    on E: Exception do
      LogStatus('Error extracting attachment: ' + E.Message);
  end;
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
procedure TFormMain.ButtonAddAttachmentClick(Sender: TObject);
var
  AttachmentName: string;
  FileData: TBytes;
  FileStream: TFileStream;
begin
  if not Pdf.Active then
  begin
    LogStatus('Please load or create a PDF document first.');
    Exit;
  end;
  
  OpenDialog.Filter := 'All Files|*.*';
  if OpenDialog.Execute then
  begin
    try
      // Read file into byte array
      FileStream := TFileStream.Create(OpenDialog.FileName, fmOpenRead);
      try
        SetLength(FileData, FileStream.Size);
        if FileStream.Size > 0 then
          FileStream.ReadBuffer(FileData[0], FileStream.Size);
      finally
        FileStream.Free;
      end;
      
      // Use filename as attachment name
      AttachmentName := ExtractFileName(OpenDialog.FileName);
      
      // Create attachment slot
      if Pdf.CreateAttachment(AttachmentName) then
      begin
        // Find the new attachment and set its data
        Pdf.Attachment[Pdf.AttachmentCount - 1] := FileData;
        
        LogStatus('Added attachment: ' + AttachmentName);
        RefreshAttachmentList;
      end
      else
        LogStatus('Failed to create attachment: ' + AttachmentName);
        
    except
      on E: Exception do
        LogStatus('Error adding attachment: ' + E.Message);
    end;
  end;
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
procedure TFormMain.ButtonDeleteAttachmentClick(Sender: TObject);
var
  Index: Integer;
  AttachmentName: string;
begin
  Index := GetSelectedAttachmentIndex;
  if Index < 0 then
  begin
    LogStatus('Please select an attachment to delete.');
    Exit;
  end;
  
  try
    AttachmentName := Pdf.AttachmentName[Index];
    
    if MessageDlg(Format('Delete attachment "%s"?', [AttachmentName]),
      mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      if Pdf.DeleteAttachment(Index) then
      begin
        LogStatus('Deleted attachment: ' + AttachmentName);
        RefreshAttachmentList;
      end
      else
        LogStatus('Failed to delete attachment.');
    end;
    
  except
    on E: Exception do
      LogStatus('Error deleting attachment: ' + E.Message);
  end;
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Number of attachments
Count := Pdf.AttachmentCount;
 
// Attachment name at index
Name := Pdf.AttachmentName[Index];
 
// Attachment data as byte array
Data := Pdf.Attachment[Index];
 
// Set attachment data
Pdf.Attachment[Index] := NewData;
 
// Create new attachment
Success := Pdf.CreateAttachment('filename.txt');
 
// Delete attachment
Success := Pdf.DeleteAttachment(Index);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
procedure CreatePdfWithAttachments;
var
  Pdf: TPdf;
  FileData: TBytes;
  FileStream: TFileStream;
begin
  Pdf := TPdf.Create(nil);
  try
    // Create new document
    Pdf.CreateDocument;
    Pdf.Active := True;
    
    // Add a page
    Pdf.AddPage(1, 595, 842);
    Pdf.PageNumber := 1;
    Pdf.AddText('Document with Attachments', 'Arial', 18, 50, 750,
      clBlack, $FF, 0.0);
    
    // Add first attachment
    FileStream := TFileStream.Create('data.csv', fmOpenRead);
    try
      SetLength(FileData, FileStream.Size);
      FileStream.ReadBuffer(FileData[0], FileStream.Size);
    finally
      FileStream.Free;
    end;
    
    if Pdf.CreateAttachment('data.csv') then
      Pdf.Attachment[0] := FileData;
      
    // Add second attachment
    FileStream := TFileStream.Create('readme.txt', fmOpenRead);
    try
      SetLength(FileData, FileStream.Size);
      FileStream.ReadBuffer(FileData[0], FileStream.Size);
    finally
      FileStream.Free;
    end;
    
    if Pdf.CreateAttachment('readme.txt') then
      Pdf.Attachment[1] := FileData;
      
    // Save
    Pdf.SaveAs('document_with_attachments.pdf');
    
  finally
    Pdf.Active := False;
    Pdf.Free;
  end;
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
procedure TFormMain.ButtonSaveDocumentClick(Sender: TObject);
var
  CurrentFileName, TempFileName: string;
  WasActive: Boolean;
begin
  SaveDialog.Filter := 'PDF Files|*.pdf|All Files|*.*';
  SaveDialog.DefaultExt := 'pdf';
  
  if SaveDialog.Execute then
  begin
    try
      CurrentFileName := Pdf.FileName;
      WasActive := Pdf.Active;
      
      // If saving to the same file, use temporary file
      if WasActive and SameText(CurrentFileName, SaveDialog.FileName) then
      begin
        TempFileName := SaveDialog.FileName + '.tmp';
        
        if Pdf.SaveAs(TempFileName) then
        begin
          // Close, delete old, rename new
          Pdf.Active := False;
          
          if DeleteFile(CurrentFileName) then
          begin
            if RenameFile(TempFileName, SaveDialog.FileName) then
            begin
              // Reload
              Pdf.FileName := SaveDialog.FileName;
              Pdf.Active := True;
              LogStatus('Document saved successfully.');
            end;
          end;
        end;
      end
      else
      begin
        // Save to different file
        if Pdf.SaveAs(SaveDialog.FileName) then
          LogStatus('Document saved: ' + ExtractFileName(SaveDialog.FileName))
        else
          LogStatus('Failed to save document.');
      end;
      
    except
      on E: Exception do
        LogStatus('Error saving document: ' + E.Message);
    end;
  end;
end;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
procedure TFormMain.ListBoxAttachmentsClick(Sender: TObject);
var
  Index: Integer;
  AttachmentName: string;
  AttachmentData: TBytes;
begin
  Index := GetSelectedAttachmentIndex;
  
  if Index >= 0 then
  begin
    AttachmentName := Pdf.AttachmentName[Index];
    AttachmentData := Pdf.Attachment[Index];
    
    MemoDetails.Lines.Clear;
    MemoDetails.Lines.Add('Name: ' + AttachmentName);
    MemoDetails.Lines.Add('Size: ' + IntToStr(Length(AttachmentData)) + ' bytes');
    MemoDetails.Lines.Add('Index: ' + IntToStr(Index));
    
    // Try to detect file type from extension
    if EndsText('.txt', AttachmentName) or
       EndsText('.csv', AttachmentName) then
      MemoDetails.Lines.Add('Type: Text file')
    else if EndsText('.pdf', AttachmentName) then
      MemoDetails.Lines.Add('Type: PDF document')
    else if EndsText('.jpg', AttachmentName) or
            EndsText('.png', AttachmentName) then
      MemoDetails.Lines.Add('Type: Image file')
    else
      MemoDetails.Lines.Add('Type: Binary file');
  end;
end;

Praktische aandachtspunten

  • Controleer invoerbestanden en foutpaden expliciet voordat u de routine in productie gebruikt.
  • Houd paginalay-out, lettertypen en coördinaten reproduceerbaar, vooral bij server-side verwerking.
  • Test het resultaat in meer dan één PDF-viewer wanneer rendering, annotaties of interactieve elementen belangrijk zijn.
  • Laat componentlevensduur en bestandshandles altijd via try/finally-achtige patronen opruimen.