Artículo técnico

Convertir un PDF a PDF/A y reparar sus metadatos

ConvertToPDFA convierte un documento ordinario en uno de archivo con una sola llamada: elimina lo que la parte elegida prohíbe, agrega lo que la parte exige, declara la parte que el documento afirma y luego verifica el resultado. La afirmación se reporta como cumplida solo cuando la verificación pasa, y GetPDFAConversionReport lista qué se hizo y qué todavía está en el camino

Esa última propiedad es la decisión de diseño en la que vale la pena detenerse. Un conversor que estampa la afirmación sin verificar es peor que ningún conversor, porque un archivo que dice ser de archivo y no lo es pasa directo por los mismos sistemas que de otro modo lo habrían detectado. El fallo aparece años después, en una auditoría, sobre un documento que nadie puede regenerar

¿Por qué un PDF con apariencia válida falla una verificación PDF/A?

La mayoría de las veces porque los dos lugares donde un PDF dice quién lo escribió no coinciden. Un validador lee tanto el diccionario de información del documento como el paquete XMP y rechaza un archivo donde difieren — y la mayoría de los archivos que fallan en este punto simplemente nunca tuvieron la mitad XMP escrita

RepairDocumentMetadata los pone de acuerdo y devuelve cuántas entradas reparó. Donde solo una mitad lleva un valor, la otra se rellena a partir de ella, así que nada de lo ya registrado se descarta. Nadie tiene que decidir qué copia es la autoritativa, porque en la práctica una copia está vacía

Hay una segunda reparación en la misma llamada que atrapa un caso más sutil. Un documento configurado a un modo PDF/A recupera su identificación de normas si se había perdido, lo cual ocurre siempre que quien llama suministra un paquete XMP propio. Sin esa identificación un validador lee el archivo como un PDF ordinario y reporta cada regla de la parte afirmada como incumplida — un fallo de aspecto espectacular con una sola causa pequeña

var
  Lib: TPDFlib;
  Repaired: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.LoadFromFile('incoming.pdf', '');
    Repaired := Lib.RepairDocumentMetadata;
    Log(Format('%d metadata entries brought into agreement', [Repaired]));
    Lib.SaveToFile('incoming-fixed.pdf');
  finally
    Lib.Free;
  end;
end;

Elegir la parte antes de convertir

SetPDFAMode y ConvertToPDFA comparten la misma numeración de modos, y tres de los valores son recientes. El modo 9 es PDF/A-4, la parte construida sobre PDF 2.0. El modo 10 es PDF/A-4e, que adicionalmente permite 3D y medios enriquecidos, y el modo 11 es PDF/A-4f, que permite un archivo incrustado de cualquier formato

La parte 4 se identifica de forma distinta a las partes anteriores: por número de parte y el año de publicación de su parte, sin letra de conformidad para PDF/A-4 puro y con la letra E o F para las dos extensiones. La verificación reconoce la parte 4, juzga sus archivos contra PDF 2.0 en vez de 1.7, y reporta un archivo de parte 4 que no declara su año de revisión

Cada archivo incrustado en un documento de parte 4 declara cómo se relaciona con el documento, como exigen tanto la parte 3 como la 4. Esta es la regla que solía atrapar a los adjuntos ordinarios: la relación se escribía solo para los adjuntos posteriores al primero y nunca para el último, así que un documento con un único adjunto — el caso común — no llevaba ninguna y fallaba la validación precisamente en ese punto

var
  Verdict: Integer;
begin
  Lib.LoadFromFile('report.pdf', '');
  Verdict := Lib.ConvertToPDFA(9);        // 9 = PDF/A-4, 10 = 4e, 11 = 4f
  Memo1.Lines.Text := Lib.GetPDFAConversionReport;
  if Verdict = 1 then
    Lib.SaveToFile('report-pdfa4.pdf')
  else
    Log('conversion incomplete - see the report for what stands in the way');
end;

Para qué sirve el reporte de conversión

Para decidir qué hacer a continuación. Una conversión que termina bien no necesita reporte; una que no termina es toda la razón por la que el reporte existe. Algunos obstáculos los puede eliminar un conversor y otros no — cifrado, contenido prohibido que transmite significado, un programa de fuente que simplemente no está en ninguna parte de la máquina. El reporte distingue lo que se hizo de lo que queda, lo que convierte «la conversión falló» en un elemento de trabajo

Trata el veredicto como la compuerta de un flujo por lotes. Convierte, lee el veredicto y rutea el archivo: archiva los que pasaron, encola el resto para una persona con el reporte adjunto. Lo que no debes hacer es guardar la salida de una conversión fallida en el archivo porque se ve mejor que la entrada — ahora lleva una afirmación que la verificación se negó a confirmar

Leer la marca que un archivo ya lleva

Antes de convertir nada, conoce lo que el documento dice de sí mismo. Una verificación PDF/A que no puede leer la marca de normas existente juzga cada archivo contra la parte 1 sin importar lo que declare, lo que significa que un documento PDF/A-2 o PDF/A-3 perfectamente válido se reporta como si no llevara marca y como de una versión demasiado alta — lo opuesto a la verdad

La marca se lee tanto si el productor la escribió como elemento XMP como si lo hizo como atributo. Ambas formas son XMP ordinario, y aceptar solo una deja a los archivos de otros productores viéndose sin marcar. Si alguna vez te has preguntado por qué un documento que valida en otras partes falla en tu propio flujo, este es un buen lugar para mirar primero

Saneamiento antes de archivar, y el bug que vale la pena conocer

La conversión de archivo y el saneamiento suelen correr juntos, porque el contenido que una política de seguridad quiere eliminar se superpone fuertemente con el contenido que PDF/A prohíbe. SanitizeDocument elimina JavaScript, y eliminar el último script también elimina el árbol de nombres vacío que este deja atrás — un árbol que de otro modo seguiría diciéndole al lector que el documento llevaba scripts

Esa segunda mitad se aprendió por las malas: un error de uno en la lista de paquetes hacía que el saneamiento reportara haber eliminado scripts sin eliminar ninguno, así que un documento saneado seguía ejecutando sus scripts al abrirse. Es un buen argumento para el principio general en el que se apoya todo este artículo — verifica el resultado en vez de confiar en la operación, tanto en tu propio flujo como en la biblioteca

Para el trabajo de archivo circundante, consulta los recorridos por el preflight de PDF/A y PDF/UA, la redacción verdadera y eliminación de contenido y los esquemas de extensión XMP de PDF/A-3 para Factur-X, que cubre el lado de los metadatos cuando el documento archivado también lleva datos estructurados de factura

PDFlibPas es una biblioteca PDF nativa en Pascal para Delphi, C++Builder y Lazarus, así que la conversión, la reparación y la validación ocurren todas dentro de tu propio proceso sin ninguna herramienta externa en la cadena — consulta la página del producto PDFlibPas para las partes de PDF/A soportadas y las plataformas