O HotXLS, a biblioteca Excel nativa para Delphi e C++Builder, foi feito para percursos de ida e volta em XLSX sem perdas: abra uma pasta de trabalho, mude uma célula, salve, e o tema personalizado do cliente, os blocos de extensão extLst estrangeiros e a cadeia de cálculo sobrevivem. Três mecanismos fazem isso funcionar — o cache literal de xl/theme/theme1.xml, a reserialização por eventos de blocos <ext> desconhecidos e um xl/calcChain.xml novo e válido segundo a especificação a cada gravação de uma pasta com fórmulas
O cenário que motiva os três é deprimentemente comum. Um serviço de faturamento carrega um modelo que o cliente desenhou no Excel — tema de cores corporativo, minigráficos em uma coluna de KPI, uma regra de formatação condicional adicionada por uma versão mais nova do Excel —, escreve o total de uma fatura na célula B3 e salva. O cliente abre o resultado e as cores da marca voltaram ao azul padrão do Office, os minigráficos sumiram e o Excel se oferece para "reparar" o arquivo. Nada no código tocou em nenhum desses recursos. A biblioteca tocou, só por ter salvado
Por que arquivos do Excel perdem formatação depois de edições por biblioteca?
Arquivos do Excel perdem formatação depois de edições por biblioteca porque a maioria das bibliotecas não edita o arquivo — ela o reconstrói. Um pacote .xlsx é um ZIP de partes XML: xl/workbook.xml, um xl/worksheets/sheetN.xml por planilha, xl/styles.xml, xl/theme/theme1.xml, xl/calcChain.xml e mais. Uma biblioteca típica analisa essas partes em um modelo de objetos na abertura e regenera cada parte a partir desse modelo na gravação. Qualquer recurso que o modelo não represente — um tema que ele nunca analisou, um bloco de extensão de um Excel mais novo — não tem onde morar na memória, então a parte regenerada o omite em silêncio
A ECMA-376 antecipou metade desse problema. O SpreadsheetML define o extLst (ECMA-376 Parte 1, a "Future Feature Data Storage Area", §18.2.10 para o elemento de nível de pasta de trabalho) como um ponto de extensão designado: produtores mais novos estacionam recursos ali, cada um embrulhado em um elemento <ext> que carrega um atributo uri identificando o recurso, e espera-se que consumidores mais antigos preservem o que não entendem. Minigráficos, segmentações e tipos mais novos de formatação condicional viajam todos desse jeito. Uma biblioteca que descarta blocos <ext> desconhecidos, portanto, não é apenas malsucedida em preservar — ela viola o contrato de compatibilidade futura em torno do qual o formato foi desenhado. A pergunta a fazer a qualquer biblioteca de planilhas que você esteja avaliando é direta: se eu mudar uma célula, o que mais muda
Como o HotXLS mantém um tema personalizado byte a byte?
O HotXLS preserva o tema de uma pasta de trabalho guardando em cache os bytes originais de xl/theme/theme1.xml na abertura e gravando-os de volta literalmente na hora de salvar. A parte de tema (ECMA-376 Parte 1, §14.2.7) é DrawingML, não SpreadsheetML — esquemas de cor, esquemas de fonte, esquemas de formato — e um motor de planilhas não tem motivo para modelá-la a fundo. Versões anteriores do HotXLS regeneravam um tema fixo do Office a cada gravação, que é exatamente a falha das "cores da marca que voltaram atrás" acima; desde a v2.89.46 o tema do pacote aberto é armazenado bruto e reemitido intocado, e o tema interno do Office é gerado apenas para pastas criadas do zero. Bytes brutos são a garantia de fidelidade mais forte possível: sem análise, sem reserialização, sem chance de desvio
A cópia literal vence deliberadamente o acesso programático ao tema. O TXLSXWorkbook expõe ThemeMajorFont e ThemeMinorFont para você escolher as famílias de título e de corpo em pastas novas, mas quando um tema literal foi capturado na abertura esses setters não têm efeito no arquivo salvo — o percurso de ida e volta tem prioridade. Se você realmente precisa alterar o tema de uma pasta existente, isso é um sinal para editar o modelo no próprio Excel, e não por uma API orientada a dados. O caso do dia a dia não precisa de API nenhuma:
var
Book: TXLSXWorkbook;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('branded-invoice.xlsx');
Book.Sheets[0].Cells[3, 2].Value := 42750.00; // a única edição
Book.SaveAs('branded-invoice-out.xlsx');
// o theme1.xml da saída é idêntico byte a byte ao da entrada
finally
Book.Free;
end;
end;
O que acontece com blocos extLst desconhecidos ao salvar?
O HotXLS captura todo bloco <ext> de nível de planilha que não modela nativamente e o reproduz no extLst da planilha salva, de modo que recursos escritos por versões mais novas do Excel sobrevivem intactos ao percurso. Desde a v2.131.0 os fragmentos capturados ficam visíveis pela propriedade somente leitura RawWorksheetExts, uma TStringList em cada planilha XLSX, o que torna a garantia auditável a partir do código de teste em vez de um ato de fé:
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
i: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('from-newer-excel.xlsx');
Sheet := Book.Sheets[0];
WriteLn(Format('%d foreign ext block(s) captured',
[Sheet.RawWorksheetExts.Count]));
for i := 0 to Sheet.RawWorksheetExts.Count - 1 do
WriteLn(Copy(Sheet.RawWorksheetExts[i], 1, 100)); // espia cada uri
finally
Book.Free;
end;
end;
O detalhe de implementação que vale conhecer é que a captura é uma reserialização no nível dos eventos, não uma cópia bruta de bytes. O leitor XML de streaming do HotXLS não expõe deslocamentos de origem, então a subárvore desconhecida é reconstruída a partir dos eventos Element, Text e EndElement conforme eles passam no fluxo. Essa abordagem esconde uma armadilha clássica: um elemento autofechado como <a/> dispara apenas um evento Element marcado como vazio e nunca um EndElement, então qualquer contador de profundidade que decremente só em EndElement nunca verá a subárvore fechar. Trate isso, e o fragmento reconstruído fica semanticamente equivalente ao original — as aspas de atributos e as formas autofechadas são normalizadas, então ele não é idêntico byte a byte, mas o Excel lê significado, não bytes. Duas propriedades da saída do próprio Excel tornam a reprodução segura: o Excel declara os atributos xmlns necessários no elemento <ext> ou dentro dele, então cada fragmento capturado é autocontido em namespaces, e é essa mesma autocontenção que permite que duplicar uma planilha dentro de uma pasta ou entre pastas leve junto os blocos estrangeiros com uma simples atribuição de lista de strings
Escrever o calcChain.xml para que o Excel confie nas suas fórmulas
O HotXLS escreve o xl/calcChain.xml (a parte Calculation Chain, ECMA-376 Parte 1, §12.3.1) sempre que a pasta salva contém fórmulas, e escolhe entre duas ordenações. Se o grafo de dependências das fórmulas já foi construído e está atualizado — você chamou Recalculate depois da última edição —, a cadeia é emitida em ordem topológica completa, dependências antes dos dependentes, com quaisquer membros de referência circular anexados ao final. Caso contrário, as células são listadas na ordem do documento. As duas estão corretas: as notas de implementação do formato pela Microsoft, [MS-XLSX], tratam a cadeia de cálculo como uma dica que o Excel verifica e reordena durante a carga, então qualquer listagem completa é legal, e o HotXLS deliberadamente se recusa a forçar a construção do grafo dentro de SaveAs — a construção de arestas é quadrática na contagem de células, um custo oculto inaceitável em uma gravação de um milhão de células
Book.Open('model.xlsx');
Book.Sheets[0].Cells[10, 4].Formula := '=SUM(D2:D9)';
// Salvo agora, o calcChain.xml lista as células de fórmula na ordem do documento.
// Depois de Recalculate o grafo de dependências existe, então a mesma gravação
// emite uma ordem topológica completa no lugar:
Book.Recalculate;
Book.SaveAs('model-out.xlsx');
Por que se importar com uma parte que o Excel trata como consultiva? Porque a ausência dela é um sinal. Alguns consumidores — heurísticas de reparo, visualizadores de terceiros, ferramentas de diff — esperam que uma pasta com fórmulas carregue uma cadeia de cálculo, e uma biblioteca que descarta a parte em silêncio ao salvar produz arquivos sutilmente diferentes de tudo que o Excel escreve. Emitir uma cadeia válida mantém a saída dentro do envelope contra o qual o resto do ecossistema foi testado, que é o núcleo silencioso e sem glamour da engenharia de ida e volta
Onde o percurso sem perdas termina
A honestidade importa mais do que um item de marketing aqui, então os limites merecem o mesmo destaque. O HotXLS não copia o pacote inteiro byte a byte: o XML das planilhas, os estilos, as strings compartilhadas e as partes da pasta de trabalho são regenerados a partir do modelo analisado, então a saída é semanticamente fiel, mas não binariamente idêntica — só os cabeçalhos locais do ZIP já carregam marcas de tempo DOS novas. Os fragmentos <ext> capturados voltam normalizados, como descrito acima. As substituições programáticas de fontes de tema são ignoradas quando existe um tema literal. E a rede de preservação tem uma malha definida: os recursos que o HotXLS modela nativamente (minigráficos, por exemplo, são analisados e reescritos, e não copiados às cegas), mais o conteúdo de extLst estrangeiro, mais as partes guardadas literalmente. Uma parte que não é modelada nem está dentro de um ponto de extensão — a parte personalizada de um suplemento exótico, digamos — fica fora dos três mecanismos que este artigo cobre, então teste os seus modelos reais em vez de supor
Um trabalho de preservação vizinho completa o quadro. Projetos VBA e referências a pastas de trabalho externas atravessam a gravação com a mesma filosofia de guardar o que não se modela, tratada no artigo complementar sobre preservação de VBA e de links externos, e as propriedades de documento em docProps têm uma API de leitura e escrita própria em vez de serem descartadas em silêncio. Quando avaliar qualquer biblioteca de planilhas, faça o teste de uma célula: abra uma pasta de produção cheia de recursos, mude um único valor, salve e compare as partes descompactadas com as do original. O que mudou além da planilha que você tocou diz mais sobre a biblioteca do que qualquer matriz de recursos
Os mecanismos de ida e volta descritos aqui — retenção literal do tema desde a v2.89.46, captura de extLst estrangeiro e emissão de calcChain.xml desde a v2.131.0 — acompanham o HotXLS Delphi Excel Component atual, cuja página de produto documenta o conjunto completo de recursos de leitura e escrita de XLSX para Delphi e C++Builder