Technical Article

Comentários de Células e Hiperligações Excel no Delphi com HotXLS

Altere o nome de uma folha de cálculo de "Summary" para "Overview" num livro gerado, e todas as hiperligações internas que apontavam para Summary!A1 deixam de funcionar. Não ocorre nenhuma exceção ao gravar, nem ao abrir. A ligação continua a ser renderizada, parece clicável, mas resolve-se silenciosamente para nada. O mesmo tipo de quebra surge após uma conversão de gravação (save-as) ou uma conversão entre os formatos .xls e .xlsx, quando um comentário fica deslocado uma coluna ou uma hiperligação relativa perde o seu destino. Ambas as funcionalidades contêm estados de revisão nos quais pessoas reais confiam para trabalhar, pelo que a falha permanece invisível até que um revisor clique e nada aconteça.

Esta é a razão prática pela qual os comentários e as hiperligações merecem mais atenção do que a sua aparência estética sugere. O HotXLS oferece ao código Delphi e C++Builder acesso de escrita direto a ambos, nos formatos XLS e XLSX, sem necessidade de automatização do Excel. A contrapartida deste controlo é a responsabilidade: a biblioteca escreve exatamente os destinos que lhe fornece e não valida nenhum deles, pelo que a preservação da integridade do fluxo de trabalho de revisão é tarefa do seu código, e não do Excel.

Comentários de células como registos de revisão automáticos

No modelo de classes XLSX, um comentário é um objeto ao nível da folha de cálculo: conhece a sua linha, a sua coluna, o autor e o corpo do texto. O campo do autor é muito importante. Quando um livro gerado pelo seu código passa por uma cadeia de revisão, a primeira pergunta de um auditor é quem escreveu determinada nota, e uma nota deixada sem autor responde a essa questão com um espaço em branco. Assinale os comentários gerados com a identidade do serviço para que a sua proveniência nunca seja ambígua.

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Note: TXLSXComment;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('reconciliation.xlsx');
    Sheet := Book.Sheets[0];

    // Authored note on the adjusted figure
    Sheet.AddComment(14, 4, 'Manual adjustment: late FX rate, see ticket FIN-2214',
      'recon-service');

    // Update an existing note instead of stacking a second one
    Note := Sheet.Comments.FindAt(14, 4);
    if Note <> nil then
      Note.Text := Note.Text + ' [verified 2026-06-11]';

    Book.SaveAs('reconciliation-reviewed.xlsx');
  finally
    Book.Free;
  end;
end;

A verificação realizada com FindAt é mais importante do que parece. Um processamento em lote que tente novamente após uma falha temporária chamará de bom grado a função AddComment uma segunda vez numa célula que já tinha sido anotada, e a célula acabará com duas notas empilhadas que ninguém solicitou. Verifique primeiro com FindAt e atualize o objeto que for devolvido. A coleção Comments também expõe DeleteAt e DeleteInRange. Essa variante de intervalo é a ideal para quando precisa de higienizar um livro antes que este saia da empresa: limpar as anotações internas de controlo de qualidade de toda uma área é feito numa única chamada, em vez de um ciclo de escrita manual sobre as células.

URLs externos e saltos dentro do livro constituem APIs diferentes

O formato OOXML armazena os dois tipos de ligações em locais diferentes. Um URL externo torna-se numa entrada de relação (relationship) na secção .rels da folha, com a célula a apontar para a relação através de um ID. Um salto interno nunca acede à camada de relações; é uma string simples de localização, como Summary!A1, armazenada diretamente na hiperligação. O HotXLS mantém essa distinção visível na API em vez de sobrecarregar um único método, o que significa que escolhe a chamada correta sabendo onde reside o destino:

Sheet.Cells[2, 1].Value := 'Source record';
Sheet.AddHyperlink(2, 1, 'https://intranet.example.com/records/2214',
  'Open record 2214', 'ERP source entry');

Sheet.Cells[3, 1].Value := 'Totals';
Sheet.AddHyperlinkToCell(3, 1, 'Overview!B12', 'Jump to totals');

No objeto TXLSXHyperlink resultante, as propriedades Url e Location excluem-se mutuamente, e IsInternal indica qual das duas está preenchida. Essa flag é o que deve verificar quando inventaria as hiperligações de um livro aberto e precisa de tratar de forma diferente os casos em que a ligação "sai do ficheiro" e em que "permanece no ficheiro": um anfitrião (host) externo pode estar sujeito a uma lista de permissões, enquanto um destino interno apenas precisa de identificar uma folha existente. As ligações internas não arrastam secções de relações, o que as torna mais eficientes para reescrever em massa.

O problema mencionado inicialmente ocorre totalmente do lado interno e decorre de um facto simples: uma string de localização não é uma referência analisada. O HotXLS escreve o texto exato que lhe é fornecido e nada reencaminha esse texto caso altere o nome da folha mais tarde. Duas defesas mostram-se eficazes na prática. A primeira é o rigor na ordenação: altere o nome de todas as folhas antes de gerar uma única hiperligação e trate os nomes das folhas como identificadores imutáveis. A segunda é mais robusta e resiste a redenominações efetuadas à posteriori. Aponte a ligação para um nome definido ao nível do livro, em vez de um endereço bruto Sheet!Cell address, pois o Excel reescreve a definição de um nome quando a folha associada é alterada, fazendo com que a hiperligação acompanhe a alteração automaticamente. Esta segunda abordagem combina-se naturalmente com as técnicas descritas em nomes definidos e fórmulas entre folhas no HotXLS.

O lado XLS: mesmos conceitos, estrutura mais antiga

A interface BIFF8 associa os comentários a intervalos (ranges) em vez de a uma coleção ao nível da folha de cálculo. Chama a função AddComment num IXLSRange e obtém um objeto TXLSComment; a propriedade Comment do intervalo lê uma nota existente, e a função ClearComments limpa-as. O aspeto complexo reside na posição. Um objeto TXLSComment não expõe publicamente a sua linha e coluna, pelo que o ciclo lógico, "percorrer todos os comentários e reportar onde estão situados", corre ao contrário na API. Tem de começar a partir das células. Ou direciona a auditoria a partir da lista de endereços que anotou, ou mantém o seu próprio registo de posições à medida que escreve, pois o objeto de comentário não lhe dirá depois onde reside.

var
  Book: IXLSWorkbook;
  Sheet: IXLSWorksheet;
  Remark: TXLSComment;
begin
  Book := TXLSWorkbook.Create;
  Sheet := Book.Sheets.Add;
  Sheet.Name := 'Review';
  Sheet.Cells.Item[5, 2].Value := 4821.50;

  Remark := Sheet.Cells.Item[5, 2].AddComment('Awaiting sign-off from controller');
  Remark.Visible := True;   // pop the note open on first view

  Sheet.AddHyperlink(7, 2, 'https://intranet.example.com/signoff/4821',
    'Sign-off form', 'Opens the controller queue');
  Book.SaveAs('review.xls');
end;

Definir a propriedade Visible como True é a forma antiga de tornar uma nota impossível de ignorar: a caixa amarela permanece aberta na folha em vez de esperar pela passagem do rato (hover). O TXLSComment vai mais longe do que o homólogo em XLSX ao expor TextRuns, permitindo que uma única nota contenha um aviso a negrito ao lado de uma explicação em texto simples, uma formatação que a API de comentários XLSX não disponibiliza da mesma forma. Las hiperligações deste lado chegam através de três sobrecargas progressivas (apenas endereço, depois com texto de exibição, e por fim com uma dica de ecrã ou screen tip) e são lidas de volta através da coleção HyperLinks da folha de cálculo, onde cada ligação disponibiliza Address, SubAddress, DisplayText e ScreenTip.

Uma folha de índice de revisão supera notas dispersas

A partir de uma dezena de anotações, ler com a passagem do rato deixa de ser uma solução viável. As notas acumulam-se em folhas que o revisor nunca chega a abrir, e as que mais importam são as mais fáceis de passar despercebidas. A estrutura mais eficaz passa por gerar uma folha de índice: uma linha por cada localização anotada, listando o nome da folha, o endereço da célula, o autor e um resumo da nota. A última coluna inclui uma hiperligação interna construída com AddHyperlinkToCell que aponta diretamente para a célula anotada. Assim, o revisor analisa uma lista em vez de procurar numa grelha, e o número de linhas desse índice funciona como o seu inventário de comentários para a fase de auditoria descrita abaixo.

O índice é simples de criar pois o seu gerador conhece todas as posições que alterou. Adicione uma estrutura (folha, linha, coluna, autor, resumo) a uma lista à medida que escreve cada comentário, e crie a folha de índice no final para que a contagem de linhas seja definitiva antes de gravar. Dois refinamentos adicionais revelam-se úteis: ordene o índice por gravidade ou por folha, em vez da sequência de inserção, e inclua uma hiperligação de retorno no cabeçalho do índice para que o revisor possa regressar ao topo após cada item. Como as hiperligações internas são strings de localização simples sem dados adicionais na camada de relações, mesmo um índice de mil linhas quase não altera o tamanho do ficheiro ou o tempo de gravação.

Essa mesma folha revela a sua utilidade no percurso de retorno. Quando o livro revisto é devolvido, o seu código lê os valores de estado introduzidos nas células ao lado das linhas do índice, em vez de voltar a analisar todas as folhas à procura de comentários alterados. Uma coluna com células de estado estruturadas é analisada de forma limpa; um conjunto disperso de notas em texto livre não o é.

Uma fase de auditoria pré-entrega que deteta as quebras

Nenhuma destas APIs valida o destino. Uma ligação para uma folha que eliminou, um anfitrião da intranet com erro ortográfico, uma partilha de ficheiros desativada no trimestre passado: todos são gravados sem qualquer aviso. A norma ECMA-376 especifica como a hiperligação é guardada, e não se a mesma aponta para algo válido. Deste modo, um livro que carregue metadados de revisão deve passar por uma breve fase de auditoria própria, executada antes de SaveAs:

  • Reúna todas as localizações internas escritas durante a geração e confirme se o nome da folha que precede o ponto de exclamação existe de facto na coleção de folhas do livro.
  • Verifique os URLs externos em relação a uma lista de permissões de esquemas e anfitriões. Caminhos simples file:// e caminhos UNC expõem detalhes do ambiente e deixam de funcionar no momento em que o ficheiro sai da sua rede.
  • Conte os comentários por folha e compare-os com o que o seu gerador pretendia escrever. Uma nova tentativa de escrita que duplicou as notas é detetada aqui em vez de na caixa de entrada do revisor.
  • Remova as anotações destinadas apenas a uso interno com DeleteInRange sempre que o destinatário do ficheiro se encontre fora da organização.

As equipas que criam os seus livros a partir de uma camada de dados podem integrar esta validação na mesma etapa do pipeline que valida os dados, fazendo com que a verificação de metadados ocorra de forma integrada. O funcionamento é o mesmo descrito no artigo exportar resultados de consultas de base de dados para relatórios Excel, aplicado a ligações e comentários em vez de linhas.

Um detalhe de aspas costuma afetar quem constrói strings de localização manualmente. Uma folha cujo nome contenha um espaço tem de ser colocada entre aspas simples na localização, exatamente da mesma forma que a barra de fórmulas as coloca: 'Quarterly Totals'!A1, e não Quarterly Totals!A1. O HotXLS aplica as mesmas regras que o motor de fórmulas utiliza para referências entre folhas, pelo que se a hiperligação funciona numa fórmula de folha de cálculo, o seu formato de aspas funcionará aqui também. Forneça um nome sem aspas e que contenha um espaço e obterá a mesma hiperligação inativa silenciosa mencionada no início.

Os comentários e as hiperligações são as partes de um livro gerado com as quais os revisores interagem sem questionar, razão pela qual um destino inativo causa problemas reais antes que alguém se aperceba. Desenvolva o processo de validação uma única vez, execute-o em cada livro antes da sua entrega e o fluxo de trabalho de revisão manter-se-á consistente apesar de renomeações e conversões. A documentação completa da API das fachadas XLS e XLSX está disponível na página do produto HotXLS Component.