Artigo Técnico

Validar Documentos Digitalizados PDF/raster em Delphi

O PDF/R, normalizado como ISO 23504-1, é o perfil PDF para documentos digitalizados: cada página transporta exatamente uma imagem de tira e nada mais. O PDFium Component valida-o a partir de Delphi, Lazarus e C++Builder através de ValidatePdfRCompliance, que lê um stream e devolve o nível de conformidade mais um conjunto de questões concretas

O perfil existe porque os digitalizadores e os sistemas de captura de documentos precisavam de um alvo mais estreito do que o PDF/A. Um PDF de arquivo pode conter tudo o que a parte permite; um PDF raster é deliberadamente empobrecido, pelo que qualquer leitor conforme o consegue mostrar de forma idêntica e qualquer escritor conforme o consegue produzir a partir de uma digitalização sem um motor de autoria

O que é que o PDF/R proíbe que o PDF/A permite?

Texto, na prática. Uma página raster transporta a imagem digitalizada e nada mais, pelo que um recurso de tipo de letra numa página é uma violação — reportada como pvriFontForbidden sob ISO 23504-1 §6.5.2. Isso surpreende quem adiciona uma camada de texto OCR invisível para pesquisa, que é uma coisa normal e útil de fazer num fluxo de trabalho PDF/A e simplesmente não é PDF/R

A relação página-imagem é igualmente estrita. A §6.5.1 faz cada página exatamente uma imagem de tira, pelo que pvriPageImageMismatch dispara quando a contagem de imagens não corresponde à contagem de páginas — uma página sem imagem e uma página com duas são ambas não conformes. E pvriBadMediaBox reporta uma página cuja MediaBox não tem a forma [0 0 w h] (§6.5.3), porque uma digitalização não tem razão para sentar-se numa origem deslocada

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;

Que codificações de imagem são permitidas

Quatro, e a lista branca é curta por uma razão. A §6.6 admite /CCITTFaxDecode, /DCTDecode, /JPXDecode e /FlateDecode — fax bivalente, JPEG, JPEG 2000 e deflate sem perdas, que entre si cobrem todas as saídas de digitalizador que importam. Todo o resto é reportado como pvriForbiddenImageFilter, incluindo /LZWDecode, /RunLengthDecode, /ASCII85Decode, /ASCIIHexDecode, /JBIG2Decode e /Crypt

Duas dessas rejeições valem a pena perceber em vez de memorizar. /JBIG2Decode comprime digitalizações bivalentes extremamente bem e é perfeitamente legal no PDF/A, mas a sua reconstrução de dicionário de símbolos consegue substituir glifos visualmente semelhantes — um modo de falha documentado para dígitos digitalizados — e um perfil cujo propósito todo é a reprodução raster fiel não pode admitir esse risco. Os filtros ASCII são excluídos pela razão oposta: inflam o ficheiro sem acrescentar nada de que um perfil raster precise

Regras de estrutura que disparam antes de qualquer página ser lida

O PDF/R também constrange o contentor. pvriObjStmPresent reporta um stream /Type /ObjStm, que o perfil proíbe completamente — streams de objeto complicam a análise sequencial simples que um leitor raster deve conseguir realizar. pvriBadHeader reporta um cabeçalho fora de %PDF-1.4 a 1.7 e %PDF-2.0, e pvriEncryptVersionMismatch reporta um ficheiro encriptado cujo cabeçalho não é %PDF-2.0, conforme §6.2.3

O catálogo e o dicionário Info estão em lista branca, não meramente verificados. pvriProhibitedCatalogEntry e pvriProhibitedInfoEntry disparam para entradas fora do conjunto permitido, e pvriInfoXmpMismatch dispara quando uma entrada Info discorda do seu equivalente XMP. Um stream /Metadata de catálogo em falta, um /ID de trailer em falta e um marcador de rodapé %PDF-raster-1.0 ausente têm cada um a sua própria questão também

Porque é que o registo de opções de gravação omite Title e Author

TPdfRSaveOptions transporta Creator, Producer, CreationDate, ModDate, DocumentId e InstanceId, e deliberadamente não tem campo para Title, Author, Subject ou Keywords. Esses quatro são as entradas que a §6.4.3 proíbe, pelo que um registo que as expusesse estaria a convidar chamadores a escrever um ficheiro não conforme através de uma API conforme

Duas opções booleanas controlam a limpeza ao converter um PDF existente. StripInfoOptionalEntries tem a predefinição de True e remove Title, Author, Subject, Keywords e Trapped do dicionário Info de origem. StripCatalogOptionalEntries também tem a predefinição de True e remove Names, Outlines, StructTreeRoot, OutputIntents, Lang e o resto, deixando apenas a lista branca da §6.3. Defina qualquer um como False e mantém as entradas — e perde conformidade, que é ocasionalmente o que um chamador genuinamente quer para um ficheiro 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;

Note o que a injeção de marcadores não faz: adiciona metadados e identificação, e não consegue fornecer conteúdo de página. Uma página de origem que não transporta nenhuma imagem de tira continua a falhar pvriPageImageMismatch depois da injeção, porque a imagem em falta nunca foi um problema de metadados

Onde o PDF/R se encaixa num pipeline de captura

Use-o onde o entregável é a própria digitalização e a fidelidade é o contrato todo — imagem probatória, captura de cheques e de remessas, arquivos de desenhos de engenharia de um digitalizador de grande formato. Use PDF/A no momento em que o documento precise de texto pesquisável, marcação, anexos embutidos ou outra coisa qualquer que o perfil raster remove

Um arranjo comum e viável é produzir ambos: um original PDF/R que nunca muda, e um derivado PDF/A com uma camada OCR para recuperação. Os validadores são independentes, pelo que o mesmo trabalho de lote consegue verificar cada artefacto contra o perfil que efetivamente reclama. Para o lado de arquivo desse par, veja as notas sobre conformidade de arquivo PDF/A e validação de preflight PDF/A, e para saída orientada à impressão o guia de validar documentos PDF/X prontos para impressão

O PDFium Component traz o motor PDFium para Delphi, C++Builder e Lazarus com uma API VCL e validadores de conformidade para PDF/A, PDF/X, PDF/E, PDF/UA e PDF/R — a página do produto PDFium Component lista as normas suportadas e versões de IDE