Artículo técnico

Trabajar con archivos adjuntos PDF en Delphi utilizando el componente PDFium

· Programación PDF

Los documentos PDF pueden contener archivos adjuntos incrustados, una función potente para combinar archivos relacionados como hojas de cálculo, imágenes o documentos de soporte. Adjunto La demostración muestra cómo trabajar con archivos adjuntos de PDF utilizando PDFium VCL.

Resumen

Esta demostración proporciona una gestión completa de archivos adjuntos, que incluye listar, extraer, agregar y eliminar archivos adjuntos de documentos PDF. Es esencial para los sistemas de gestión de documentos que necesitan manejar archivos PDF complejos.

Características principales

  • Listar archivos adjuntos – Ver todos los archivos incrustados en un PDF.
  • Extraer archivos adjuntos – Guardar archivos adjuntos en el disco.
  • Agregar adjuntos. – Incrustar nuevos archivos en archivos PDF.
  • Eliminar adjuntos. – Eliminar adjuntos de archivos PDF.
  • Ver detalles. – Ver nombres y tamaños de los adjuntos.

Requisitos de la DLL PDFium

Antes de ejecutar cualquier aplicación PDFium VCL, asegúrese de que los archivos DLL de PDFium estén instalados:

  • pdfium32.dll / pdfium64.dll – Versiones estándar (~5-6 MB)
  • pdfium32v8.dll / pdfium64v8.dll – Con el motor JavaScript V8 (~23-27 MB)

Instalación: Ejecutar PDFiumVCL\DLLs\CopyDlls.bat como administrador para copiar automáticamente los archivos DLL a los directorios del sistema de Windows.

Listado de adjuntos.

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;

Extracción de adjuntos.

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;

Agregar adjuntos.

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;

Eliminar adjuntos.

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;

Acceder a las propiedades del adjunto.

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);

Crear un nuevo documento con adjuntos.

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;

Guardar el documento después de las modificaciones.

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;

Mostrar los detalles del adjunto.

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;

Casos de uso.

  • Paquetes de documentos. – Combinar el documento principal con archivos de soporte.
  • Envío de formularios. – Adjuntar evidencia o documentos de respaldo a los formularios.
  • Manuales técnicos. – Incluir archivos CAD, hojas de cálculo o código fuente.
  • Documentos legales. – Adjuntar anexos y apéndices.
  • Gestión de archivos. – Extraer archivos adjuntos para su procesamiento.

Conclusión.

La demostración de adjuntos muestra cómo PDFium VCL proporciona un control completo sobre los archivos adjuntos de PDF. Ya sea que esté construyendo un sistema de gestión de documentos o procesando formularios PDF con archivos integrados, estas API lo hacen sencillo.

La capacidad de agregar, extraer y eliminar adjuntos de forma programática abre muchas posibilidades para la automatización y los flujos de trabajo de procesamiento de documentos.

Administre los adjuntos de PDF con facilidad utilizando Delphi PDFium VCL..