Artigo Técnico

Exportação e Importação de XFDF no Delphi com o Componente PDFium

O Componente PDFium para Delphi e Lazarus troca dados de formulário e anotações através do XFDF, o formato de intercâmbio XML definido pela ISO 19444-1. O TPdf.ExportXFDF serializa cada valor de campo de formulário e cada anotação suportada no documento carregado para um arquivo XFDF ou TStream; o TPdf.ImportXFDF lê um documento XFDF de volta, recriando as anotações em suas páginas e aplicando os valores dos campos. Essa única ida e volta cobre dois fluxos de trabalho que todo aplicativo de documento eventualmente precisa: um revisor marca um PDF no Acrobat e envia os comentários por e-mail para você mesclar, ou uma regra de conformidade diz que os dados do formulário devem residir em um arquivo separado, comparável (diffable) e auditável, em vez de serem incorporados no próprio PDF

O PDFium suporta importação e exportação de XFDF?

A biblioteca PDFium em si não suporta. A API C nativa não possui nenhuma função XFDF ou FDF — nada em seus cabeçalhos públicos lê ou grava qualquer um dos formatos, de modo que nenhuma quantidade de empacotamento (wrapping) o levará até lá. O Componente PDFium, portanto, implementa todo o mecanismo XFDF em Pascal, em uma unidade dedicada que constrói e analisa o XML diretamente sobre o modelo de dados de anotação e campo de formulário do TPdf existente. O gravador emite o XML manualmente e o leitor é um analisador de máquina de estados escrito à mão, de modo que o recurso não adiciona dependência do Delphi XMLDoc ou das unidades DOM do FPC e se comporta de maneira idêntica no Delphi e no Lazarus

Essa escolha de design importa quando você avalia alternativas. Se você tem chamado funções FPDF_* puras do Pascal, o XFDF é uma barreira: a capacidade simplesmente não está na DLL. O componente cruza essa barreira tratando o XFDF como um problema de serialização puro — ele coleta os valores dos campos e os registros de anotação que o wrapper já sabe ler, os grava como XML padrão e inverte o processo na importação

O que contém um arquivo XFDF?

A ISO 19444-1 define o XFDF como a representação XML do FDF, e sua carga útil divide-se em dois blocos de nível superior. O elemento <fields> carrega nomes de campos de formulário hierárquicos e seus valores — tudo o que um usuário digitou, escolheu ou marcou em um AcroForm. O elemento <annots> carrega as anotações: o TPdf.ExportXFDF emite 18 subtipos — text, highlight, underline, strikeout, squiggly, line, circle, square, caret, polygon, polyline, stamp, ink, freetext, fileattachment, sound, link e redact. As anotações do tipo Widget estão deliberadamente ausentes de <annots> porque seus dados viajam em <fields>, e os subtipos internos do visualizador, como Popup, não fazem parte do vocabulário do XFDF

Para tornar a ida e volta fiel, o registro TPdfAnnotation foi estendido com os metadados esperados pelo XFDF: Name (o identificador exclusivo NM), Subject, ModificationDate, CreationDate, Icon, Opacity, pontos de extremidade de linha, vértices de polígono e polilinha e caminhos de gestos de tinta. Cada novo campo faz par com uma sentinela booleana Has*, de modo que o código que construiu anotações em versões anteriores continua compilando e incluindo os mesmos dicionários — um campo não definido nunca é gravado. Se você já cria marcações de forma programática, o mesmo registro que você usa para anotações de marcação de texto com quad points agora carrega tudo o que o XFDF precisa

Como exportar dados de formulário e anotações para XFDF?

Uma única chamada faz todo o documento. Carregue o PDF com o preenchimento de formulário ativado, chame ExportXFDF e o componente percorrerá cada página, coletará os valores dos campos e anotações e gravará o XML. O valor de retorno é o número de bytes UTF-8 gravados, o que serve como uma verificação rápida de integridade nos logs

var
  Pdf: TPdf;
  Bytes: Integer;
begin
  Pdf := TPdf.Create(Self);
  Pdf.FormFill := True;            // needed so field values are live
  Pdf.FileName := 'expense-report.pdf';
  Pdf.Active := True;
  Bytes := Pdf.ExportXFDF('expense-report.xfdf');
  ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;

Ambas as direções possuem uma sobrecarga de TStream, portanto nada força a criação de um arquivo temporário no disco. A exportação para um TMemoryStream é o formato natural quando o XFDF é destinado a uma resposta HTTP, um blob de banco de dados ou uma entrada de arquivo assinada

var
  Buffer: TMemoryStream;
begin
  Buffer := TMemoryStream.Create;
  try
    Pdf.ExportXFDF(Buffer);        // same XML, no file involved
    Buffer.Position := 0;
    // hand the stream to a web response, blob column, or zip entry
  finally
    Buffer.Free;
  end;
end;

Como o ImportXFDF mescla os comentários de volta no documento?

O lado da importação é onde o fluxo de trabalho do revisor se fecha. Um colega faz anotações no contrato no Acrobat, exporta os comentários como XFDF e envia a você alguns kilobytes de XML em vez de uma segunda cópia do PDF. O TPdf.ImportXFDF analisa esse arquivo, cria cada anotação na página nomeada pelo seu atributo page e grava os valores dos campos nas anotações do tipo widget correspondentes. A função retorna a contagem combinada de campos mais anotações aplicadas, para que a interface do usuário possa confirmar exatamente quanto chegou

var
  Applied: Integer;
begin
  Applied := Pdf.ImportXFDF('review-comments.xfdf');
  StatusBar.SimpleText :=
    Format('%d fields and annotations merged', [Applied]);
end;

O analisador é um digitalizador de tags escrito à mão, projetado para tolerância em vez de rigor: elementos desconhecidos são ignorados, instruções de processamento, declarações DOCTYPE e comentários são ignorados, nomes de sinalizadores não reconhecidos são descartados, e os valores de atributos são desescapados por meio de todo o conjunto de referências de entidades XML e caracteres numéricos, incluindo pares substitutos UTF-16. O XFDF produzido pelo Acrobat, por um visualizador web ou por outra ferramenta é mesclado sem cerimônias. Uma vez que as anotações estejam no documento, o ciclo de revisão continua com a engrenagem de resposta e status descrita em construindo um fluxo de trabalho de revisão de anotações com o Componente PDFium, e os valores de campos importados participam da lógica normal de ordem de tabulação abordada em navegação por campos de formulário

Por que os separadores decimais importam para o XFDF?

Cada anotação no XFDF é posicionada por strings de coordenadas — como rect="70.5,540,200,560" —, e a ISO 19444-1 exige o ponto como o separador decimal. A formatação de ponto flutuante padrão do Delphi segue a localidade do Windows, de modo que em um sistema alemão ou francês uma chamada simples de FloatToStr transforma 70.5 em 70,5, o que corrompe uma lista de coordenadas separada por vírgulas em dados inválidos que outros processadores rejeitam ou leem incorretamente. O Componente PDFium normaliza cada número que grava: o separador decimal local é reescrito como ponto e os zeros à direita são removidos, de modo que o arquivo exportado é idêntico em nível de bytes, seja produzido em uma máquina en-US ou de-DE. Em uma situação de suporte, é importante saber que o componente gerencia isso para você

Quais são os limites da ida e volta?

Dois limites merecem ser declarados claramente. Primeiro, o XFDF transporta dados de anotação, e não aparências renderizadas — os fluxos de aparência não fazem parte do formato, de modo que o visualizador receptor regenera a aparência de cada anotação a partir de suas propriedades. Um destaque ou quadrado será renderizado corretamente em qualquer lugar; um selo com aparência personalizada voltará ao que o visualizador de destino desenha para o nome desse selo. Segundo, a API PDFium subjacente possui setters para strings, mas nenhum para números de anotação ou geometria de caminho, de modo que Opacity, pontos de extremidade de linha, vértices e gestos de tinta são apenas para exportação: o componente os preserva fielmente ao gravar o XFDF, mas não pode gravá-los de volta em um PDF na importação. Valores de campos, conteúdos, cores, retângulos, quad points, datas e metadados de identidade realizam a ida e volta em ambas as direções

Para uma introdução prática, o exemplo XfdfLab é enviado nos conjuntos de demonstração do Delphi, C++Builder e Lazarus e vincula ambas as chamadas a botões sobre qualquer PDF que você abrir. O suporte a XFDF está incluído na versão atual do Componente PDFium, ao lado das APIs de anotação e formulário nas quais ele se baseia