Technical Article

HotXLS: Proteção de Folhas, Configuração de Página e Impressão em Delphi

Três grupos de definições de folhas de cálculo nada têm a ver com os valores das células e tudo a ver com o comportamento do ficheiro após sair do seu código. A proteção de folhas determina quais as células que um utilizador pode editar após a entrega do livro. A configuração de página define a orientação, o tamanho do papel e as margens. As definições de impressão (linhas de título repetidas, escala e quebras de página manuais) controlam como uma grelha de comprimento arbitrário é impressa em papel. Nenhuma das três é visível ao analisar os dados num visualizador simples, e as três falham silenciosamente em produção quando estão incorretas. O HotXLS, uma biblioteca nativa de folhas de cálculo para Delphi e C++Builder, expõe a totalidade destas definições para .xls e .xlsx, o que significa que também reproduz todas as regras contra-intuitivas do Excel integradas nessa área.

A primeira dessas regras surpreende quase toda a gente na primeira vez que protege uma folha gerada. Chame Protect e, de repente, ninguém consegue escrever em nenhuma célula, incluindo as colunas de entrada em torno das quais construiu o livro. O seu código não mexeu nessas colunas, e é exatamente por isso que isso acontece.

Todas as células nascem bloqueadas

A norma ECMA-376 define locked como parte do registo de formatação de uma célula, não como uma propriedade da proteção em si, e o seu padrão é verdadeiro. A proteção da folha é apenas o interruptor que torna a flag aplicável. Assim, toda a grelha carrega uma flag de bloqueio desde o momento em que existe, de forma adormecida, e a chamada a Protect ativa todas elas em simultâneo. A solução é definir a ordem deliberadamente: construa o layout, desbloqueie explicitamente os intervalos que os utilizadores devem editar e aplique a proteção no fim.

Book := TXLSXWorkbook.Create;
try
  Sheet := Book.Sheets.Add('Timesheet');
  // ... header row, name column, and rate formulas written here ...
  Sheet.Range['B2:B50'].SetLocked(False);         // staff type hours here
  Sheet.Range['F2:F50'].SetFormulaHidden(True);   // keep the rate math private
  Sheet.Protect('review-2026');                   // now the lock flags bite
  Book.SaveAs('timesheet.xlsx');
finally
  Book.Free;
end;

O SetFormulaHidden faz algo diferente e fácil de ignorar: enquanto a proteção estiver ativa, a célula continua a mostrar o seu valor calculado, mas a barra de fórmulas não exibe nada. Isto é relevante quando uma fórmula incorpora tarifas de faturação, margens ou pesos de classificação que prefere não disponibilizar a todos os destinatários que clicam num total. Na fachada XLS, o mesmo objetivo é expresso por intervalo através de IXLSRange.Locked e FormulaHidden. A folha de cálculo também disponibiliza nessa fachada quinze flags do tipo Allow* (AllowSort, AllowAutoFilter, AllowFormatCells, entre outras), permitindo que uma folha protegida continue a ser ordenada e filtrada, em vez de ficar congelada numa apresentação estática.

O que a palavra-passe de proteção realmente protege

Ambos os formatos armazenam a palavra-passe de proteção de folhas e livros como um hash legado de 4 dígitos hexadecimais. Dezasseis bits significam que inúmeras strings colidem com qualquer palavra-passe fornecida, e as ferramentas de remoção estão à distância de uma pesquisa na web. Trate a proteção como um cinto de segurança contra edições acidentais, não como um controlo de acessos. É a ferramenta certa para evitar que os revisores escrevam por cima da coluna de fórmulas e a ferramenta errada para qualquer cenário que envolva a palavra confidencial.

Um nível acima, o ProtectWorkbook na fachada XLSX bloqueia a estrutura do livro, impedindo a adição, renomeação, eliminação ou reordenação de folhas. Utilize-o sempre que a lista de folhas seja um contrato com um leitor a jusante que indexa as folhas por nome ou posição. Uma folha renomeada quebra a importação do outro lado tão de certeza como uma coluna eliminada. A fachada XLS espelha esta hierarquia com TXLSWorkbook.Protect ao nível do livro e chamadas Protect por folha, além de uma propriedade isProtected para código que necessita de inspecionar um ficheiro herdado antes de o modificar.

Quando o requisito é a verdadeira confidencialidade, o mecanismo muda por completo. O SaveAsEncrypted produz um pacote encriptado com AES sob o esquema ECMA-376 Standard Encryption, abordado em detalhe no guia de output XLSX protegido por AES, e a fachada legacy XLS escreve e lê ficheiros .xls encriptados com RC4 através de EncryptionPassword e da sobrecarga de password de Open. A diferença não é académica. Uma folha protegida viaja em texto limpo, pelo que qualquer ferramenta zip pode ler os valores das suas células, ao passo que um pacote encriptado é ilegível sem a palavra-passe. Uma diretiva de auditoria que afirme 'o ficheiro de folha de pagamentos deve ser protegido' significa quase sempre enencriptação, independentemente do vocabulário que utilize.

A configuração de página faz parte do contrato do documento

O comportamento de impressão é invisível no ecrã, razão pela qual é entregue com falhas com tanta frequência. No momento em que um cliente imprime o livro, ou o exporta para PDF para um auditor, as margens, a escala e os títulos repetidos tornam-se requisitos funcionais que ninguém testou. Na fachada XLSX, estas definições dependem diretamente da folha de cálculo:

Sheet.PageLandscape := True;
Sheet.PaperSize := xlsxPaperA4;
Sheet.SetPageMargins(0.5, 0.5, 0.75, 0.75, 0.3, 0.3);
Sheet.CenterHeader := 'Monthly Timesheet';
Sheet.RightFooter := 'Page &P of &N';
Sheet.PrintArea := '$A$1:$F$60';     // bare reference: no sheet name here
Sheet.PrintTitleRows := '$1:$1';     // header row repeats on every page
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;              // grow downward as the data grows
Sheet.PrintGridlines := False;

Duas destas linhas escondem armadilhas. As strings de cabeçalho e rodapé utilizam os códigos de formatação do Excel: &P para a página atual, &N para a contagem total, com &L, &C e &R para endereçar as três secções explicitamente. A outra armadilha é o PrintArea, que recebe de propósito uma referência de célula simples. O HotXLS armazena-a sem qualificação e adiciona o nome da folha como prefixo quando escreve o ficheiro, pelo que passar 'Timesheet!$A$1:$F$60' manualmente produz uma referência duplamente qualificada e malformada. A mesma precaução aplica-se um nível abaixo: as áreas e títulos de impressão são persistidos sob os nomes definidos integrados do Excel _xlnm.Print_Area e _xlnm.Print_Titles, pelo que nunca deve adicionar entradas _xlnm.* manualmente através de DefinedNames, sob pena de os dois mecanismos colidirem pelo mesmo espaço.

Escala que sobrevive a volumes de dados de produção

A combinação FitToWidth := 1 com FitToHeight := 0 traduz-se como 'ajustar sempre as colunas à largura de uma página, e utilizar tantas páginas verticais quantas os dados exigirem', sendo o padrão correto para qualquer relatório cuja contagem de linhas varie. A armadilha é ajustar uma percentagem fixa ou um par de ajuste à página num ficheiro de testes com trinta linhas: aplique as mesmas definições a seiscentas linhas de produção e o output explodirá em dezenas de páginas cortadas ou encolherá abaixo do legível. Dimensione a largura, permita o crescimento em comprimento e repita a linha de cabeçalho através de PrintTitleRows para que a página dezassete continue legível de forma isolada.

As quebras manuais seguem a mesma disciplina de regeneração de tudo o resto num livro gerado. AddRowBreak(BeforeRow) inicia uma nova página antes do limite de uma secção, mas quando o gerador é reexecutado e as linhas se deslocam, uma quebra desatualizada cai a meio da tabela. Chame primeiro ClearAllPageBreaks e depois adicione novamente as quebras calculadas com base nos contadores de linhas do próprio gerador, em vez de corrigir posições antigas. Na fachada XLS, os controlos equivalentes residem em Sheet.PageSetup (orientação, tamanho do papel, margens, strings de cabeçalho e rodapé, ajuste de páginas), com RepeatRows e RepeatColumns a cobrir os títulos de impressão.

Verificar o resultado antes do cliente o fazer

Os erros de proteção e impressão partilham uma propriedade: são triviais de verificar manualmente e quase nunca são testados. Abra o ficheiro gerado no Excel e dedique-lhe noventa segundos. Tente escrever numa célula de entrada e confirme que ela aceita a digitação; tente escrever numa célula bloqueada e confirme que surge o aviso de proteção; verifique se uma fórmula oculta deixa a barra de fórmulas vazia. Em seguida, execute a Pré-visualização de Impressão com um conjunto de dados de dimensão de produção, não com uma amostra de trinta linhas, e confirme o número de páginas, a linha de título repetida e a numeração do rodapé. A pré-visualização é o passo que compensa o esforço, porque a geometria de impressão depende de definições sem representação visual no ecrã e, sem recorrer a uma impressora física, é o único local onde um erro de escala se torna visível.

Uma última definição completa a revisão. O FreezePane(ACol, ARow) mantém o bloco de cabeçalho visível enquanto o revisor faz scroll. Isso é comportamento de ecrã e não de impressão, mas o revisor avalia o trabalho como um todo. E um livro de cálculo que começa por ser um layout desenhado por um designer ganha a maior parte disto de forma gratuita: o fluxo de trabalho de geração de relatórios baseada em modelos mantém a configuração de página no modelo, onde um humano a ajustou face a uma impressora real, restando ao código preencher os dados e reaplicar a proteção após a estruturação do layout.

O HotXLS é uma biblioteca nativa em Object Pascal para Delphi e C++Builder; a referência completa da API de proteção e configuração de página encontra-se na página do Componente HotXLS.