Artigo Técnico

Importar e Exportar Anotações PDF como XFDF no Delphi

O HotPDF exporta e importa anotações de PDF como XFDF através de duas funções que atuam no documento atualmente carregado: ExportLoadedAnnotationsToXFDF e ImportLoadedAnnotationsFromXFDF. O XFDF is o formato XML de intercâmbio de anotações normalizado como ISO 19444-1, e este par permite que um programa em Delphi ou C++Builder entregue os seus comentários ao Acrobat ou a uma ferramenta de revisão de terceiros e receba os resultados marcados de volta, tudo sem reescrever o conteúdo da página onde as anotações se encontram

Imagine as duas direções que isto resolve. Um revisor abre o relatório gerado no Acrobat, coloca uma seta vermelha numa figura desalinhada, circula um total errado, escreve uma nota na margem e, em seguida, exporta os comentários para um pequeno ficheiro XFDF. Ou o inverso: o seu programa produz as marcações e necessita de as enviar a alguém cuja ferramenta não seja o HotPDF. De qualquer forma, as anotações viajam como XML que ambos os lados compreendem, e as páginas do PDF permanecem byte a byte exatamente o que eram

Qual é a diferença entre FDF e XFDF?

O FDF e o XFDF transportam a mesma carga útil (payload) em duas sintaxes diferentes, e a distinção é importante no momento em que decide qual o ficheiro a entregar a outra ferramenta. O FDF é o Forms Data Format mais antigo definido dentro da própria especificação de PDF: utiliza a sintaxe de objetos PDF, pelo que um ficheiro FDF assemelha-se a um PDF simplificado e precisa de um analisador compatível com PDF para ser lido. O XFDF é a expressão XML desses mesmos dados, normalizado independentemente como ISO 19444-1, o que significa que qualquer biblioteca XML em qualquer plataforma o pode abrir, comparar (diff) ou gerar. Ambos os formatos podem conter valores de campos de formulário numa árvore <fields>, que a cláusula 6.3 da norma ISO 19444-1 rege, e anotações numa árvore <annots>; o HotPDF divide essas responsabilidades, encaminhando os dados do formulário através do ExportLoadedFormToXFDF e reservando o ExportLoadedAnnotationsToXFDF para o lado de <annots>. Quando troca comentários com um web service, un servidor de revisão Java ou um script, o XFDF é o formato que não obriga o outro lado a incorporar um analisador de PDF

Como exportar anotações PDF como XFDF no Delphi?

O HotPDF exporta anotações percorrendo cada página do documento carregado, emitindo um elemento XFDF para cada anotação suportada e devolvendo o número de anotações gravadas. Carregue o PDF primeiro e, em seguida, chame o ExportLoadedAnnotationsToXFDF com um caminho de destino. O resultado inteiro é a contagem de anotações serializadas; um resultado igual ou inferior a zero significa que nada foi exportado e nenhum ficheiro foi gravado, o que é o seu sinal de que o documento não continha anotações de um subtipo suportado

var
  Pdf: THotPDF;
  Written: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('report-reviewed.pdf', '') > 0 then
    begin
      // Escreve um elemento XFDF por anotação suportada em cada página
      Written := Pdf.ExportLoadedAnnotationsToXFDF('comments.xfdf');
      if Written <= 0 then
        ShowMessage('No supported annotations were found');
    end;
  finally
    Pdf.Free;
  end;
end;

O XFDF que resulta é XML simples e legível. O HotPDF escreve uma raiz <xfdf> no espaço de nomes ISO 19444-1, um contentor <annots> e um filho por anotação com o seu índice de página baseado em zero, cor e geometria como atributos ou elementos filhos. Uma linha com preenchimento amarelo e uma ponta de seta aberta, ao lado de um polígono preenchido, é serializada desta forma

<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/">
  <annots>
    <line page="0" start="72,700" end="220,700"
          color="#FF0000" interior-color="#FFFF00"
          head="OpenArrow" tail="None">
      <contents-richtext>A linha de base parece desalinhada</contents-richtext>
    </line>
    <polygon page="0" color="#0000FF" interior-color="#CCE5FF">
      <vertices>72,120;180,120;180,200;72,200</vertices>
    </polygon>
  </annots>
</xfdf>

Como os subtipos de anotação são mapeados para elementos XFDF

Cada subtipo de anotação é mapeado para um elemento ISO 19444-1 específico com a sua própria convenção de geometria, e o HotPDF segue essas estruturas em vez de inventar as suas próprias. As anotações de linha contêm um atributo start e um end contendo os dois pares de coordenadas de extremidade, retirados diretamente da matriz L da anotação, enquanto os estilos de fim de linha LE se tornam atributos head e tail. As anotações de polígono e polilinha movem a sua lista de pontos para um elemento filho <vertices> como pares x,y separados por ponto e vírgula, e não como um atributo, porque um leitor que espera o elemento filho ignorará silenciosamente pontos ocultos em qualquer outro lugar. As anotações de tinta (ink), que podem conter vários traços separados, aninham um elemento <inklist> com um filho <gesture> por traço, para que uma assinatura com múltiplos traços sobreviva à viagem como gestos distintos em vez de um único bloco (blob) fundido

O rich text, a cor e o estilo de contorno (border) sobrevivem juntamente com a geometria. O corpo de rich text de uma nota é gravado como um elemento filho <contents-richtext>; o preenchimento interior que o PDF armazena na matriz IC, a pintura dentro de um círculo, quadrado, polígono ou ponta de seta de linha e o preenchimento de uma caixa de redação chegam como um atributo interior-color no formato #RRGGBB; e a largura do contorno, padrão de traço (dash pattern) e um efeito de contorno nublado (cloudy) mapeiam para os atributos width, dashes, style e intensity, para que uma chamada com contorno nublado continue a ser lida como nublada na outra extremidade. O HotPDF também preserva a janela popup associada a uma anotação de marcação, importando a geometria do filho popup e o seu estado aberto ou fechado para o dicionário Popup da anotação, e transporta os estados aberto e de revisão da anotação de texto, de modo que um documento revisto mantém não apenas as suas formas, mas também os metadados do fluxo de trabalho em que os revisores confiam

Importar o XFDF de volta para um documento carregado

O HotPDF importa XFDF analisando o XML, criando uma nova anotação para cada elemento através de NewLoadedAnnotation, associando-a à página que o elemento indica e devolvendo quantas anotações adicionou. O fluxo de trabalho é simétrico com a exportação: carregue o PDF de base, chame o ImportLoadedAnnotationsFromXFDF com o ficheiro do revisor e, em seguida, guarde o documento carregado para persistir as novas marcações. Se o ficheiro estiver em falta ou o XML não for analisado, a função devolve zero e o documento carregado permanece inalterado

var
  Pdf: THotPDF;
  Added: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('report.pdf', '') > 0 then
    begin
      Added := Pdf.ImportLoadedAnnotationsFromXFDF('comments.xfdf');
      if Added > 0 then
        Pdf.SaveLoadedDocument('report-annotated.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

Como cada elemento XFDF indica o seu próprio índice de página, as anotações vão parar às páginas contra as quais foram criadas, mesmo quando importa vários ficheiros em sequência, tornando seguro recolher comentários de mais do que um revisor no mesmo documento carregado antes de uma única gravação. O exemplo abaixo reúne dois revisores numa única cópia fundida. Para criar e editar os próprios objetos de anotação em código, em vez de os trocar como ficheiros, consulte como o HotPDF cria e edita objetos de anotação PDF diretamente a partir do Delphi

var
  Pdf: THotPDF;
  Total, I: Integer;
  Files: array[0..1] of string;
begin
  Files[0] := 'alice-comments.xfdf';
  Files[1] := 'bob-comments.xfdf';
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('master.pdf', '') > 0 then
    begin
      Total := 0;
      for I := Low(Files) to High(Files) do
        Inc(Total, Pdf.ImportLoadedAnnotationsFromXFDF(Files[I]));
      if Total > 0 then
        Pdf.SaveLoadedDocument('master-merged.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

O que faz a viagem de ida e volta de forma limpa e o que não faz

O HotPDF completa a viagem de ida e volta dos subtipos de anotação que a norma ISO 19444-1 acolhe, e ignora deliberadamente os restantes em vez de emitir algo que um leitor possa interpretar incorretamente. O conjunto suportado abrange os tipos de marcação que dominam o trabalho real de revisão: notas de texto, texto livre, linha, quadrado, círculo, polígono, polilinha, os quatro tipos de marcação de texto (realce, sublinhado, rasurado e ondulado), carimbo, tinta e cursor (caret), além de anexo de ficheiro, som, redação e ligação (link), somando dezoito subtipos ao todo. Uma anotação cujo subtipo fique fora dessa lista é ignorada na exportação e, como é ignorada em vez de ser gravada em branco, não inflaciona a contagem que a função devolve

O rich text é a advertência sincera. O HotPDF preserva o corpo do <contents-richtext> para que o texto estilizado e os conteúdos simples completem a viagem, mas o XFDF transporta o texto da anotação e a marcação de estilo, não um fluxo de aparência renderizado, pelo que a aplicação recetora desenha novamente a popup com as suas próprias fontes e layout, em vez de reproduzir os píxeis exatos do HotPDF. Trate a viagem de ida e volta como fiel ao conteúdo e à intenção, e não à renderização no ecrã ao nível do píxel. Se o seu conteúdo estilizado reside em dados de formulários XFA em vez de fluxos de anotações, as regras diferem, e como o HotPDF lida com XFA exData, rich text e hiperligações aborda esse caminho separado

O tratamento ao nível do carácter é mais rigoroso do que parece, o que é exatamente o que deseja. O HotPDF aplica as regras de escape da cláusula 5.8.2 da norma ISO 19444-1 quando grava texto, codificando os caracteres significativos para XML e bytes de controlo para que um comentário contendo um E comercial (&), um parêntese angular (< ou >) ou uma quebra de linha produza XML bem formado que qualquer analisador em conformidade aceite, e reverte as mesmas regras na importação. É por isso que uma nota colada de uma folha de cálculo, com pontuação e tudo, regressa intacta em vez de corromper o ficheiro

O intercâmbio de anotações é apenas uma parte do que a API de documentos carregados faz, e combina-se com o resto: importe o XFDF de um revisor, ajuste as páginas ou edite os metadados do documento, achate ou altere as permissões do ficheiro e, em seguida, exporte um XFDF novo para a ronda seguinte. Tudo isto é fornecido com o HotPDF Component padrão para Delphi e C++Builder, cuja referência documenta a cobertura total de subtipos de anotação e as funções XFDF de dados de formulário associadas