As hiperligações PDF são anotações URI: um retângulo que cobre uma determinada área da página que, ao ser clicado, indica ao visualizador para abrir um URL. A anotação e o texto abaixo dela são objetos completamente independentes. O método PrintHyperlink do HotPDF agrupa ambos numa única chamada, desenhando o texto e calculando o retângulo da anotação com base nas métricas do texto renderizado. Esta conveniência esconde um pormenor que vale a pena compreender antes de escrever código de produção.
Como funciona o PrintHyperlink
O PrintHyperlink reside em THPDFPage e aceita quatro argumentos: coordenadas X e Y (em pontos, origem no canto inferior esquerdo, com Y a crescer para cima), a string de etiqueta (label) a desenhar e o URL de destino. Internamente, chama TextOut na cor atual da hiperligação e, em seguida, calcula imediatamente o retângulo da anotação a partir de TextWidth e TextHeight nas métricas de tipo de letra atuais. Isso significa que o tipo de letra e o tamanho têm de ser definidos antes da chamada, e não devem sofrer alterações entre o desenho da etiqueta e a inserção da anotação, porque ambos são resolvidos na mesma chamada.
A cor predefinida é clBlue. O método SetRGBHyperlinkColor altera-a apenas para as chamadas seguintes; não atualiza retroativamente anotações já escritas. Se necessitar de cores diferentes para diferentes grupos de ligações na mesma página, chame SetRGBHyperlinkColor antes de cada grupo e restaure o valor original depois.
Eis um documento mínimo que escreve três ligações com duas cores diferentes:
procedure CreateLinkedReport(const FileName: string);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := FileName;
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 11);
// Default blue for informational links
Pdf.CurrentPage.TextOut(50, 750, 0, 'Reference links:');
Pdf.CurrentPage.PrintHyperlink(50, 720, 'Product page', 'https://www.loslab.com/en-us/pdf-library/delphi-pdf-component.html');
Pdf.CurrentPage.PrintHyperlink(50, 695, 'Online manual', 'https://www.loslab.com/en-us/pdf-library/documentation.html');
// Red for the action link
Pdf.CurrentPage.SetRGBHyperlinkColor(clRed);
Pdf.CurrentPage.PrintHyperlink(50, 660, 'Purchase license', 'https://www.loslab.com/en-us/order/');
Pdf.CurrentPage.SetRGBHyperlinkColor(clBlue); // restore default
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
A armadilha das coordenadas
O HotPDF utiliza uma origem no canto inferior esquerdo com Y a crescer para cima, medida em pontos (1/72 polegada). Uma página A4 mede 595 x 842 pt; uma página US Letter mede 612 x 792 pt. Y=750 situa-se próximo do topo de uma página A4, e Y=50 estaria próximo da margem inferior. Quem vem da área de gráficos de ecrã ou HTML assume o oposto e coloca a primeira linha de ligação diretamente fora da área visível.
O retângulo de anotação que o PrintHyperlink calcula utiliza o mesmo sistema de coordenadas. Se mais tarde rodar a página, a redimensionar ou alterar o tamanho da página sem recalcular os valores X/Y, o texto visível e o retângulo clicável irão afastar-se. A ligação "funciona" no sentido de que clicar em algum local perto do texto ativa o URL, mas a zona ativa deixa de corresponder ao que o leitor vê. Teste no tamanho real da página e no nível de zoom com que irá distribuir o documento, e não apenas na máquina de desenvolvimento a 100%.
Um caso em que o desvio é garantido: se chamar o PrintHyperlink com coordenadas adequadas para uma página A4 e depois mudar para uma página com formato estreito personalizado sem ajustar os valores X/Y, a anotação pode acabar por ficar totalmente fora da página. O objeto da anotação continua a ser escrito no PDF; a maioria dos visualizadores recorta-o silenciosamente, pelo que a ligação simplesmente desaparece sem qualquer erro.
Texto da etiqueta versus URL de destino
Os argumentos Text e Link são independentes. Pode desenhar "Download invoice PDF" enquanto o destino é um URL HTTPS totalmente qualificado com parâmetros de consulta. Esta separação é intencional; a etiqueta visível deve ser legível por humanos e o URL pode ser longo ou gerado dinamicamente.
O que cria problemas é quando a etiqueta é o próprio URL em bruto, especialmente se for longo. Se o URL quebrar visualmente em duas linhas mas o retângulo de anotação tiver sido calculado para uma string de linha única, apenas a primeira linha será clicável. O PrintHyperlink não lida com fluxos multilinha; mantenha a etiqueta curta o suficiente para caber numa única linha no tamanho de letra atual e largura de página, ou utilize uma etiqueta descritiva curta com o URL completo como destino.
Para documentos que serão arquivados ou distribuídos sem uma ligação ativa à internet, considere também se o próprio URL deve aparecer de forma impressa em algum lugar no corpo do documento, e não apenas como metadados de anotação. Um leitor que imprima o PDF em papel não tira qualquer partido de uma anotação URI.
Um exemplo completo de geração de documento
O padrão abaixo mostra um cenário mais realista: gerar um relatório curto com uma secção de cabeçalho, texto do corpo e uma linha de rodapé com ligações, tudo a partir de código em vez de um formulário com campos TEdit:
procedure GenerateProductSheet(
const FileName, ProductName, ProductURL, SupportURL: string);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Compression := cmFlateDecode;
Pdf.BeginDoc;
// Header
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 750, 0, WideString(ProductName));
// Body paragraph placeholder
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 710, 0, 'See the links below for full documentation.');
// Footer links
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 80, 0, 'Links:');
Pdf.CurrentPage.PrintHyperlink(50, 60, 'Product page', ProductURL);
Pdf.CurrentPage.PrintHyperlink(200, 60, 'Support', SupportURL);
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Note que o SetFont é chamado antes de cada grupo de chamadas de texto. O tipo de letra não persiste após a chamada a AddPage, e se se esquecer de o definir antes de PrintHyperlink numa página nova, o retângulo de anotação será calculado com base nas métricas predefinidas da página, que podem diferir do que espera.
Onde a gestão de anotações varia entre visualizadores
As anotações URI em PDF são definidas na ISO 32000-1 §12.6.4.7, e todos os visualizadores conformes as devem seguir. Na prática, alguns comportamentos diferem de visualizador para visualizador. O Adobe Acrobat exibe um aviso de segurança no primeiro clique para URLs que não estejam na lista de domínios fidedignos; muitos navegadores e leitores leves não o fazem. Alguns visualizadores de PDF empresariais em ambientes restritos desativam por completo as anotações URI através de políticas de segurança, pelo que o clique nada faz, sem qualquer erro visível. As aplicações móveis de PDF variam quanto a abrir as ligações dentro da vista web da aplicação ou passá-las para o navegador do sistema.
Nenhum deste problemas são bugs que possa corrigir do lado da geração; são decisões de política do visualizador. O que pode fazer é escrever etiquetas de ligação que também tornem o URL visível no corpo do documento, para que um leitor num ambiente restrito possa copiar o endereço manualmente. A anotação é a conveniência; o texto é a alternativa de recurso.
Outro detalhe que vale a pena saber: as anotações URI em PDF não possuem qualquer sublinhado visual por defeito. O sublinhado que vê na maioria dos visualizadores é desenhado pelo próprio programa com base no tipo de anotação, e não por um glifo no fluxo de conteúdo. Se necessitar de um sublinhado físico que sobreviva à impressão num renderizador não interativo ou à conversão de PDF para imagem, desenhe-o explicitamente com LineTo e Stroke no offset Y adequado abaixo da linha de base do texto. Trata-se de uma operação de desenho separada, que o PrintHyperlink não trata por si.
A API de hiperligações apresentada aqui faz parte do HotPDF Component para Delphi e C++Builder.