Artículo técnico

Validar documentos escaneados PDF/raster en Delphi

PDF/R, normalizado como ISO 23504-1, es el perfil PDF para documentos escaneados: cada página porta exactamente una imagen de banda y nada más. PDFium Component lo valida desde Delphi, Lazarus y C++Builder a través de ValidatePdfRCompliance, que lee un flujo y devuelve el nivel de conformidad más un conjunto de problemas concretos

El perfil existe porque los escáneres y los sistemas de captura de documentos necesitaban un objetivo más estrecho que PDF/A. Un PDF archivable puede contener cualquier cosa que la parte permita; un PDF raster es deliberadamente desnudo, de modo que cualquier lector conforme lo muestra de forma idéntica y cualquier escritor conforme puede producirlo a partir de un escaneo sin un motor de autoría

¿Qué prohíbe PDF/R que PDF/A permite?

Texto, en la práctica. Una página raster porta la imagen escaneada y nada más, así que un recurso de fuente en una página es una infracción —reportada como pvriFontForbidden bajo ISO 23504-1 §6.5.2—. Eso sorprende a quien añade una capa de texto OCR invisible para busquabilidad, que es algo normal y útil en un flujo PDF/A y simplemente no es PDF/R

La relación página-imagen es igual de estricta. El §6.5.1 hace que cada página sea exactamente una imagen de banda, así que pvriPageImageMismatch se dispara cuando el recuento de imágenes no coincide con el de páginas —una página sin imagen y una página con dos son ambas no conformes—. Y pvriBadMediaBox informa de una página cuyo MediaBox no tiene la forma [0 0 w h] (§6.5.3), porque un escaneo no tiene ningún motivo para asentarse en un origen desplazado

uses FPdfPdfr;

var
  Src: TFileStream;
  Res: TPdfRValidationResult;
begin
  Src := TFileStream.Create('scan-batch-0142.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfRCompliance(Src);
    if Res.IsCompliant then
      Memo1.Lines.Add('PDF/R-1 conformant')
    else
    begin
      if pvriFontForbidden in Res.Issues then
        Memo1.Lines.Add('A page names a font resource; a raster page carries no text');
      if pvriPageImageMismatch in Res.Issues then
        Memo1.Lines.Add('Image count does not match page count');
      if pvriForbiddenImageFilter in Res.Issues then
        Memo1.Lines.Add('A strip image uses an encoding outside the white list');
    end;
  finally
    Src.Free;
  end;
end;

Qué codificaciones de imagen se permiten

Cuatro, y la lista blanca es corta por un motivo. El §6.6 admite /CCITTFaxDecode, /DCTDecode, /JPXDecode y /FlateDecode —fax bivalente, JPEG, JPEG 2000 y deflate sin pérdida, que entre todos cubren cualquier salida de escáner que importe—. Todo lo demás se informa como pvriForbiddenImageFilter, incluidos /LZWDecode, /RunLengthDecode, /ASCII85Decode, /ASCIIHexDecode, /JBIG2Decode y /Crypt

Dos de esos rechazos conviene entenderlos en lugar de memorizarlos. /JBIG2Decode comprime los escaneos bivalentes extremadamente bien y es perfectamente legal en PDF/A, pero su reconstrucción por diccionario de símbolos puede sustituir glifos visualmente parecidos —un modo de fallo documentado para dígitos escaneados— y un perfil cuyo propósito íntegro es la reproducción raster fiel no puede admitir ese riesgo. Los filtros ASCII se excluyen por el motivo opuesto: inflan el archivo sin añadir nada que un perfil raster necesite

Reglas de estructura que se disparan antes de leer ninguna página

PDF/R también constriñe el contenedor. pvriObjStmPresent informa de un flujo /Type /ObjStm, que el perfil prohíbe en redondo —los flujos de objeto complican el análisis simple y secuencial que un lector raster debe poder realizar—. pvriBadHeader informa de una cabecera fuera de %PDF-1.4 a 1.7 y %PDF-2.0, y pvriEncryptVersionMismatch informa de un archivo cifrado cuya cabecera no es %PDF-2.0, según §6.2.3

El catálogo y el diccionario Info están en lista blanca, no simplemente comprobados. pvriProhibitedCatalogEntry y pvriProhibitedInfoEntry se disparan con entradas fuera del conjunto permitido, y pvriInfoXmpMismatch se dispara cuando una entrada Info discrepa de su equivalente XMP. Un flujo /Metadata ausente en el catálogo, un /ID ausente en el trailer y un marcador de pie %PDF-raster-1.0 ausente tienen cada uno su propio problema

Por qué el registro de opciones de guardado omite Title y Author

TPdfRSaveOptions porta Creator, Producer, CreationDate, ModDate, DocumentId e InstanceId, y deliberadamente no tiene campo para Title, Author, Subject ni Keywords. Esas cuatro son las entradas que §6.4.3 prohíbe, así que un registro que las expusiera estaría invitando a los llamadores a escribir un archivo no conforme a través de una API conforme

Dos opciones booleanas controlan la limpieza al convertir un PDF existente. StripInfoOptionalEntries vale True por defecto y retira Title, Author, Subject, Keywords y Trapped del diccionario Info de origen. StripCatalogOptionalEntries también vale True por defecto y retira Names, Outlines, StructTreeRoot, OutputIntents, Lang y el resto, dejando únicamente la lista blanca del §6.3. Pon cualquiera a False y conservas las entradas —y pierdes la conformidad, que ocasionalmente es justamente lo que un llamador quiere para un archivo interno—

var
  Opts: TPdfRSaveOptions;
  Src, Dest: TFileStream;
begin
  Opts := TPdfRSaveOptions.Default;
  Opts.Creator := 'Capture Station 4';
  Opts.Producer := 'PDFium Component';
  Src := TFileStream.Create('scan-in.pdf', fmOpenRead or fmShareDenyWrite);
  try
    Dest := TFileStream.Create('scan-pdfr.pdf', fmCreate);
    try
      InjectPdfRMarkers(Src, Dest, Opts);   // markers + metadata, not page content
    finally
      Dest.Free;
    end;
  finally
    Src.Free;
  end;
end;

Ten en cuenta lo que la inyección de marcadores no hace: añade metadatos e identificación, y no puede suministrar contenido de página. Una página de origen que no porte imagen de banda seguirá fallando pvriPageImageMismatch después de la inyección, porque la imagen ausente nunca fue un problema de metadatos

Dónde encaja PDF/R en un flujo de captura

Úsalo donde el entregable es el propio escaneo y la fidelidad es el contrato íntegro —imagen probatoria, captura de cheques y remesas, archivos de planos de ingeniería procedentes de un escáner de gran formato—. Usa PDF/A en cuanto el documento necesite texto buscable, etiquetado, adjuntos incrustados o cualquier otra cosa que el perfil raster elimine

Un arreglo habitual y viable es producir ambos: un PDF/R original que nunca cambia, y un derivado PDF/A con una capa OCR para recuperación. Los validadores son independientes, así que el mismo trabajo por lotes puede comprobar cada artefacto contra el perfil que de verdad reclama. Para el lado archivable de esa pareja, consulta las notas sobre la conformidad archivable PDF/A y la validación de preflight PDF/A, y para la salida orientada a impresión el recorrido por la validación de documentos PDF/X listos para imprimir

PDFium Component lleva el motor PDFium a Delphi, C++Builder y Lazarus con una API VCL y validadores de conformidad para PDF/A, PDF/X, PDF/E, PDF/UA y PDF/R; la página del producto PDFium Component lista las normas admitidas y las versiones de IDE