O HotPDF exporta e importa anotações PDF como XFDF através de duas funções que atuam sobre o documento atualmente carregado, ExportLoadedAnnotationsToXFDF e ImportLoadedAnnotationsFromXFDF. O XFDF é o formato XML de troca de anotações normalizado como ISO 19444-1, e este par permite a um programa Delphi ou C++Builder entregar os seus comentários ao Acrobat ou a uma ferramenta de revisão de terceiros e receber de volta os resultados anotados, tudo sem reescrever o conteúdo das páginas sobre as quais as anotações assentam
Imagine os dois sentidos que isto resolve. Um revisor abre o seu relatório gerado no Acrobat, larga uma seta vermelha sobre uma figura desalinhada, rodeia um total errado e escreve uma nota na margem, e depois exporta os comentários para um pequeno ficheiro XFDF. Ou o inverso: o seu programa produz ele próprio as marcações, e precisa de as enviar a alguém cuja ferramenta não é o HotPDF. De uma forma ou de outra, as anotações viajam como XML que ambos os lados entendem, e as páginas do PDF ficam exatamente o que eram, byte a byte
Qual é a diferença entre FDF e XFDF?
O FDF e o XFDF transportam a mesma carga em duas sintaxes diferentes, e a distinção conta no momento em que decide que ficheiro entregar a outra ferramenta. O FDF é o antigo Forms Data Format definido dentro da própria especificação PDF: usa a sintaxe de objetos do PDF, pelo que um ficheiro FDF se parece com um PDF reduzido ao osso e precisa de um analisador que perceba de PDF para ser lido. O XFDF é a expressão XML desses mesmos dados, normalizada de forma independente como ISO 19444-1, o que significa que qualquer biblioteca XML em qualquer plataforma o consegue abrir, comparar ou gerar. Ambos os formatos podem transportar valores de campos de formulário numa árvore <fields>, que a cláusula 6.3 da ISO 19444-1 rege, e anotações numa árvore <annots>; o HotPDF separa essas responsabilidades, encaminhando os dados de formulário por ExportLoadedFormToXFDF e reservando ExportLoadedAnnotationsToXFDF para o lado <annots>. Quando troca comentários com um serviço web, um servidor de revisão em Java ou um script, o XFDF é o formato que não obriga o outro lado a integrar um analisador de PDF
Como se exportam anotações PDF como XFDF em Delphi?
O HotPDF exporta anotações percorrendo todas as páginas do documento carregado, emitindo um elemento XFDF por cada anotação suportada, e devolvendo o número de anotações escritas. Carregue primeiro o PDF, e depois chame ExportLoadedAnnotationsToXFDF com um caminho de destino. O resultado inteiro é a contagem de anotações serializadas; um resultado de zero ou inferior significa que nada foi exportado e nenhum ficheiro foi escrito, o que é o seu sinal de que o documento não tinha 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 sai é XML simples e legível. O HotPDF escreve uma raiz <xfdf> no espaço de nomes da ISO 19444-1, um contentor <annots>, e um filho por anotação com o seu índice de página com base em zero, a cor e a geometria como atributos ou elementos filhos. Uma linha com preenchimento amarelo e uma ponta de seta aberta, ao lado de um polígono preenchido, serializa-se assim
<?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>Baseline looks off</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 se mapeiam em elementos XFDF
Cada subtipo de anotação corresponde a um elemento específico da ISO 19444-1 com a sua própria convenção de geometria, e o HotPDF segue essas estruturas em vez de inventar as suas. As anotações de linha transportam um atributo start e um atributo end com os dois pares de coordenadas das extremidades, tirados diretamente do array L da anotação, enquanto os estilos de terminação de linha LE se tornam atributos head e tail. As anotações de polígono e de polilinha passam a sua lista de pontos para um elemento filho <vertices> na forma de pares x,y separados por ponto e vírgula, e não para um atributo, porque um leitor que espera o elemento filho descarta em silêncio os pontos escondidos noutro sítio qualquer. As anotações de tinta, que podem conter vários traços separados, aninham um elemento <inklist> com um filho <gesture> por traço, para que uma assinatura de vários traços sobreviva à viagem como gestos distintos e não como uma mancha fundida
O texto formatado, a cor e o estilo da margem sobrevivem a par da geometria. O corpo em texto formatado de uma nota é escrito como um filho <contents-richtext>; o preenchimento interior que o PDF guarda no array IC, a pintura dentro de um círculo, quadrado, polígono ou ponta de seta e o preenchimento de uma caixa de expurgo, chega como atributo interior-color na forma #RRGGBB; e a largura da margem, o padrão de tracejado e o efeito de margem em nuvem correspondem aos atributos width, dashes, style e intensity, pelo que uma chamada de atenção com contorno em nuvem continua a ler-se como nuvem do outro lado. O HotPDF preserva também a janela de destaque associada a uma anotação de marcação, importando a geometria do filho de destaque e o seu estado aberto ou fechado para o dicionário Popup da anotação, e transporta os estados de abertura e de revisão das anotações de texto, para que um documento revisto mantenha não só as suas formas mas também os metadados de fluxo de trabalho em que os revisores se apoiam
Importar XFDF de volta para um documento carregado
O HotPDF importa XFDF analisando o XML, criando uma anotação nova para cada elemento através de NewLoadedAnnotation, ligando-a à página que o elemento indica, e devolvendo quantas anotações acrescentou. O fluxo de trabalho é simétrico ao da exportação: carregue o PDF base, chame ImportLoadedAnnotationsFromXFDF com o ficheiro do revisor, e depois grave o documento carregado para tornar as marcações permanentes. Se o ficheiro não existir ou o XML não puder ser analisado, a função devolve zero e o documento carregado fica intocado
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 aterram nas páginas contra as quais foram criadas, mesmo quando importa vários ficheiros em sequência, o que torna seguro reunir comentários de mais do que um revisor no mesmo documento carregado antes de uma única gravação. O exemplo abaixo funde dois revisores numa cópia combinada. Para construir e editar os próprios objetos de anotação em código, em vez de os trocar como ficheiros, veja 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 ida e volta sem problemas, e o que não faz
O HotPDF faz a ida e volta dos subtipos de anotação a que a ISO 19444-1 dá casa, e salta deliberadamente os restantes em vez de emitir algo que um leitor interpretaria mal. 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, mais anexo de ficheiro, som, expurgo e ligação, dezoito subtipos ao todo. Uma anotação cujo subtipo fique fora dessa lista é ignorada na exportação e, por ser saltada em vez de escrita vazia, não inflaciona a contagem que a função devolve
O texto formatado é a ressalva honesta. O HotPDF preserva o corpo <contents-richtext>, pelo que o texto estilizado e o conteúdo simples fazem a viagem, mas o XFDF transporta o texto de um comentário e a sua marcação de estilo, não um fluxo de aparência já representado, pelo que a aplicação recetora volta a desenhar a janela de destaque com as suas próprias fontes e o seu próprio layout em vez de reproduzir os píxeis exatos do HotPDF. Encare a ida e volta como fiel ao conteúdo e à intenção, não à representação no ecrã ao nível do píxel. Se o seu conteúdo estilizado vive em dados de formulário XFA e não em fluxos de anotação, as regras são outras, e como o HotPDF trata exData, texto formatado e hiperligações do XFA descreve esse caminho separado
O tratamento ao nível dos caracteres é mais rigoroso do que parece, que é exatamente o que se pretende. O HotPDF aplica as regras de escape da cláusula 5.8.2 da ISO 19444-1 quando escreve texto, codificando os caracteres significativos para XML e os bytes de controlo, para que um comentário que contenha um E comercial, um sinal de menor ou maior ou uma quebra de linha produza XML bem formado que qualquer analisador conforme aceita, e inverte as mesmas regras na importação. É por isso que uma nota colada de uma folha de cálculo, com toda a sua pontuação, regressa intacta em vez de corromper o ficheiro
A troca de anotações é uma fatia do que a API de documento carregado faz, e compõe-se com o resto: importar o XFDF de um revisor, ajustar as páginas ou editar os metadados do documento, achatar ou repor as permissões do ficheiro, e depois exportar um XFDF novo para a ronda seguinte. Tudo isto faz parte do HotPDF Delphi Component padrão para Delphi e C++Builder, cuja referência documenta toda a cobertura de subtipos de anotação e as funções XFDF companheiras para dados de formulário