Artículo técnico

PDFlibPas: compliance and signing workbench in Delphi

Esta versión localizada revisa Building a Compliance and Signing Workbench in Delphi with PDFlibPas tomando como referencia el artículo base en inglés ya actualizado para equipos que trabajan con Delphi, PDF y software documental

La página convierte la versión base actualizada en puntos de control prácticos para diseño, implementación y validación

Contenido sincronizado desde la versión base

El artículo base se amplió con contexto práctico, decisiones técnicas y ejemplos concretos, por lo que esta página debe leerse como una guía de trabajo y no como un resumen breve

Puntos importantes de la versión actualizada:

  • Trabaje primero con archivos de entrada pequeños y reproducibles
  • Mantenga intactos nombres de producto, API, archivos y valores literales
  • Guarde la salida del validador y las versiones junto con el archivo de prueba generado

Decisiones prácticas de implementación

Empiece por el tipo de archivo, el resultado esperado y el error que debe ver el usuario. Después vincule cada llamada API con un resultado verificable para que validación, registros y soporte puedan reproducir el caso del cliente

  • Trabaje primero con archivos de entrada pequeños y reproducibles
  • Mantenga intactos nombres de producto, API, archivos y valores literales
  • Guarde la salida del validador y las versiones junto con el archivo de prueba generado

Código y puntos API

Los ejemplos de código se conservan sin cambios para poder compararlos directamente con proyectos Delphi, C++Builder y Lazarus/FPC

var
  PDF: TPDFlib;
  ListID, I: Integer;
begin
  PDF := TPDFlib.Create;
  try
    ListID := PDF.CheckFileCompliance('invoice-fixed.pdf', '', 1, 0);  // 1 = PDF/A
    if ListID = 0 then
    begin
      if PDF.LastErrorCode <> 0 then
        raise Exception.Create('Preflight could not read the file')
      else
        Writeln('No PDF/A findings');
    end
    else
    begin
      for I := 0 to PDF.GetStringListCount(ListID) - 1 do
        Writeln(PDF.GetStringListItem(ListID, I));
      PDF.ReleaseStringList(ListID);
    end;
  finally
    PDF.Free;
  end;
end;
ProcessID := PDF.NewSignProcessFromFile('invoice-fixed.pdf', '');
if ProcessID = 0 then
  raise Exception.Create('Cannot open source for signing');
PDF.SetSignProcessField(ProcessID, 'ApprovalSig');
PDF.SetSignProcessPFXFromFile(ProcessID, 'company.pfx', PfxPassword);
PDF.SetSignProcessInfo(ProcessID, 'Invoice approval', 'Berlin', 'billing@example.com');
PDF.SetSignProcessCustomSubFilter(ProcessID, 'ETSI.CAdES.detached');  // PAdES baseline
PDF.SetSignProcessDigestAlgorithm(ProcessID, 2);                      // SHA-256
PDF.SetSignProcessReserveContentsBytes(ProcessID, 8192);              // room for a later timestamp
PDF.EndSignProcessToFile(ProcessID, 'invoice-signed.pdf');
if PDF.GetSignProcessResult(ProcessID) <> 1 then
  Writeln('Sign failed, code ', PDF.GetSignProcessResult(ProcessID));
PDF.ReleaseSignProcess(ProcessID);
var
  Doc: TPDFlibSignDoc;
  Names: TStringList;
  FS: TFileStream;
  I: Integer;
  SourceSize, RangeStart, GapStart, TailStart, TailLen: Int64;
begin
  // Capture the size before Open: the audit object holds a share lock on the file
  FS := TFileStream.Create('invoice-signed.pdf', fmOpenRead or fmShareDenyNone);
  SourceSize := FS.Size;
  FS.Free;
  Doc := TPDFlibSignDoc.Create;
  Names := TStringList.Create;
  try
    if not Doc.Open('invoice-signed.pdf', '', False) then Exit;
    Doc.GetSignatureFieldNames(Names);
    for I := 0 to Names.Count - 1 do
      if Doc.GetSignatureValueObjNum(Names[I]) > 0 then  // > 0 means the field is signed
      begin
        RangeStart := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 11)));
        GapStart   := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 12)));
        TailStart  := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 13)));
        TailLen    := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 14)));
        if (RangeStart = 0) and (TailStart + TailLen = SourceSize) then
          Writeln(Names[I], ': signature covers the file to EOF')
        else
          Writeln(Names[I], ': earlier revision, or unusual ByteRange layout');
      end;
    Doc.Close;
  finally
    Names.Free;
    Doc.Free;
  end;
end;

Comprobación antes de publicar

Revise el archivo de salida con las mismas herramientas que usará el cliente o el archivo documental. Registre versión del componente, datos de prueba, versión del validador y resultado observado