Un backend de reporting Delphi qui émet du .xlsx depuis des années hérite d'une nouvelle exigence : les règles de marchés publics d'un client du secteur public imposent une sortie OpenDocument Spreadsheet, et les analystes de ce compte renvoient leurs modifications sous forme de fichiers .ods enregistrés depuis LibreOffice. Le même code doit donc désormais écrire de l'ODS et le relire. HotXLS, la bibliothèque tableur native en Object Pascal de losLab pour Delphi et C++Builder, gère les deux sens sans qu'Excel ni LibreOffice soient installés nulle part. Les deux sens sont proches mais pas identiques. Depuis HotXLS 2.88.229, le chemin d'import ramène les formules sous forme de formules vivantes à côté de leurs résultats mis en cache, et les versions actuelles lisent aussi les styles, les formats de nombre, les plages fusionnées, les commentaires, les images, les graphiques et les mises en forme conditionnelles. Ce qui ne survit toujours pas, ce sont la sémantique OpenFormula complète et une poignée de détails de paquet, si bien qu'une équipe qui suppose un miroir parfait rencontrera quand même des cas limites quelque part entre la révision du client et le rapport suivant, généralement sans aucune erreur à montrer du doigt
La prise en charge ODS vit sur la façade XLSX, pas sur celle de XLS
HotXLS livre deux hiérarchies de classes indépendantes dans un seul paquet : TXLSWorkbook dans l'unité lxHandle pour les fichiers .xls binaires BIFF8, et TXLSXWorkbook dans l'unité lxHandleX pour les paquets OOXML .xlsx. Chaque point d'entrée OpenDocument — OpenODS, SaveAsODS, GetODSSheetNames — pend de TXLSXWorkbook. Ce placement n'a rien d'arbitraire. Un paquet ODS, tel que spécifié dans OASIS ODF 1.3, est une archive zip portant un membre mimetype, un manifeste et un corps content.xml, ce qui en fait un cousin structurel du zip OOXML ; BIFF8 est un flux binaire d'enregistrements des années 1990 qui n'a rien de commun avec cela
Ce placement a une conséquence pratique : un classeur .xls hérité ne peut pas devenir .ods en un seul appel. Vous faites d'abord passer le contenu BIFF dans le modèle XLSX, avec SaveXLSWorkbookAsXLSX de l'unité lxXlsxExport, vous rouvrez le résultat par TXLSXWorkbook, puis vous exportez depuis là. La passerelle n'est pas sans perte, et il vaut mieux en connaître les manques avant de bâtir dessus. Elle copie les valeurs, les formules, les formats de nombre, les polices, les remplissages, les bordures, les plages fusionnées, les largeurs de colonnes et les hauteurs de lignes. Elle abandonne les commentaires, les graphiques et la mise en forme conditionnelle. Une source .xls à la mise en forme chargée atteindra l'ODS plus dépouillée qu'elle en est partie, et c'est une propriété de la passerelle, pas de l'écrivain ODS
La détection est automatique côté import. La simple méthode Open reconnaît un paquet ODS à son membre mimetype, en se rabattant sur un contrôle du content.xml de premier niveau quand ce membre est absent, si bien qu'un chemin de code générique du type « ouvrir ce que l'utilisateur a déposé » n'a pas besoin de renifler lui-même les extensions. Après ouverture, la propriété SourceFormat indique quelle branche a joué
Exporter vers ODS avec TODSExportOptions
L'appel d'export tient en une ligne ; l'objet d'options qui l'entoure porte les décisions sur lesquelles un relecteur reviendra plus tard :
var
Book: TXLSXWorkbook;
Opts: TODSExportOptions;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-report.xlsx');
Opts := TODSExportOptions.Create; // l'appelant le possède et le libère
try
Opts.Generator := 'ReportService 4.2'; // remplacement de meta:generator
Opts.IncludeCharts := True;
Opts.IncludeImages := True;
Book.SaveAsODS('quarterly-report.ods', Opts);
finally
Opts.Free;
end;
finally
Book.Free;
end;
end;
L'objet d'options appartient à l'appelant. HotXLS ne le libérera pas, ce qui explique pourquoi le try..finally interne est là et n'est pas facultatif. Les deux propriétés qui changent la sortie, au lieu de simplement l'étiqueter, méritent un examen plus attentif. Régler IncludeCharts := False fait plus que masquer les graphiques : cela retire du paquet les sous-documents de graphiques et leurs entrées de manifeste, ce qui est exactement ce que vous voulez quand le consommateur est un pipeline de données qui trébucherait dessus. Generator remplace la chaîne ODF meta:generator, qui indique sinon HotXLS/<version> ; remplacez-la quand un outillage en aval identifie les producteurs de fichiers pour aiguiller le support. Si rien de tout cela ne s'applique, laissez complètement tomber l'objet d'options. Appeler SaveAs(FileName, xlsxOpenDocumentSpreadsheet) revient au même que SaveAsODS avec les valeurs par défaut, et les surcharges sur flux des deux méthodes vous laissent écrire le paquet droit dans une réponse HTTP sans fichier temporaire
Ce que le chemin d'import lit — et ce qu'il saute encore
Lisez cette partie attentivement avant de promettre à quiconque une fidélité en aller-retour. L'import ODS dans HotXLS a commencé comme un chemin allégé qui ne gardait que les valeurs scalaires et les résultats de formules mis en cache, et il a depuis bien dépassé ce stade. Les versions actuelles lisent content.xml avec styles.xml et settings.xml : valeurs scalaires et résultats de formules mis en cache, lignes et colonnes répétées déployées dans la grille, plages fusionnées, texte enrichi, hyperliens, commentaires, images et graphiques incorporés, styles de cellules hérités et par défaut, formats de nombre et de date, hauteurs de lignes et largeurs de colonnes, plages nommées, volets figés, plages de filtre automatique, validations de données et mises en forme conditionnelles. Ce qu'OpenODS ne convertit toujours pas, ce sont la sémantique OpenFormula complète, les clés de protection ODS, la géométrie des commentaires, la mise en forme avancée des commentaires et les fonctionnalités de paquet extérieures au contenu tableur
Les formules sont la partie qui risque le plus de mordre. Une cellule ODF range deux choses côte à côte : l'expression de formule, écrite dans le dialecte OpenFormula défini en ODF 1.3 partie 4, et la dernière valeur que l'application productrice a calculée pour elle. Les premières versions ne lisaient que la valeur mise en cache. Depuis HotXLS 2.88.229, OpenODS importe aussi l'expression : une cellule écrite avec le préfixe of:= ou oooc:= conserve sa valeur mise en cache et reçoit une formule Excel normalisée, les références entre crochets du type [.A1:.B1] et les séparateurs d'arguments points-virgules étant convertis en syntaxe Excel. Depuis 2.384.32, l'import convertit aussi les lignes de tableaux en ligne ({1;2|3;4} devient {1,2;3,4}) et l'opérateur d'union de références ~, et depuis 2.384.55 une telle union se relit sans une seconde paire de parenthèses. Traduire OpenFormula en syntaxe Excel reste un problème de conversion de dialecte avec de vrais cas limites autour du vocabulaire des fonctions, de la syntaxe des références et des modèles d'erreurs, d'où la valeur mise en cache qui accompagne le tout : les nombres importés sont exactement ceux que l'expéditeur a vus en dernier
Le mode de défaillance autour duquel concevoir s'est réduit en conséquence. Avant 2.88.229, une feuille dont les totaux étaient corrects quand LibreOffice l'a enregistrée en dernier s'importait avec des nombres corrects devenus des constantes : modifiez une cellule d'entrée, recalculez, et rien ne bougeait. Sur les versions actuelles, les formules reviennent vivantes, si bien que le risque restant est une construction que le traducteur ne modélise pas, ce qui peut laisser une formule qui se recalcule différemment de l'application productrice tandis que la valeur mise en cache affiche encore le résultat de l'expéditeur. Comparez les résultats recalculés aux valeurs mises en cache importées pour les formules dont votre pipeline dépend, et rétablissez toute formule en désaccord à partir de vos propres règles métier via Cell.Formula, qui sur la façade XLSX prend l'expression sans signe égal initial
Concevoir autour des manques résiduels de l'aller-retour
L'export produit son rendu à partir du modèle de classeur complet en mémoire : valeurs, formules, styles et, si vous les demandez, graphiques et images. L'export des formules a été resserré de version en version. Depuis 2.384.55, les tableaux en ligne, les unions de références et les références absolues gardent leur sens en OpenFormula ; depuis 2.384.56, HotXLS déclare les espaces de noms de ses préfixes de formules, si bien que LibreOffice évalue les formules au lieu d'afficher #VALUE! dans chaque cellule à formule ; et depuis 2.384.65, les références à colonnes et à lignes entières sont écrites sous la forme OpenFormula [.A:.A], si bien qu'Excel ne les ouvre plus comme des #NAME?. Les mises en forme conditionnelles ont suivi : depuis 2.384.66, les règles à base de formule sont écrites en formula-is(...) avec leur cellule de base, ce que LibreOffice reconnaît, et depuis 2.384.69, les règles de valeur et de formule sont aussi écrites sous forme de conditions standard style:map, la seule forme que lise Excel, tandis que l'import lit les deux formes. Un cycle complet .xlsx vers .ods vers .xlsx sur une version actuelle conserve donc valeurs, formules, styles et mises en forme conditionnelles ; ce qu'il peut encore perdre se situe dans les manques listés dans la section précédente
Book := TXLSXWorkbook.Create;
try
Book.Open('vendor-revision.ods'); // format détecté automatiquement
if Book.SourceFormat = xlsxOpenDocumentSpreadsheet then
begin
// Depuis HotXLS 2.88.229, les formules reviennent vivantes à côté de
// leurs résultats mis en cache ; rétablissez avant d'enregistrer toute
// formule dont le pipeline aval ne peut pas considérer la traduction fiable.
Book.Sheets[0].Cells[2, 5].Formula := 'SUM(B2:D2)';
Book.SaveAs('vendor-revision.xlsx');
end;
finally
Book.Free;
end;
Le motif d'architecture qui en découle : traitez les fichiers .ods entrants comme des flux de données, non comme des documents à modifier sur place. Gardez le classeur canonique en .xlsx, lisez les valeurs dans les révisions clients, et émettez du ODS frais à la demande depuis la copie canonique. La vérification appartient aux deux camps — ouvrez les fichiers exportés dans LibreOffice Calc, le consommateur ODF de référence, et dans Excel, qui lit l'ODS depuis des années mais diverge de LibreOffice aux marges de la prise en charge des graphiques et des styles. Le nombre de feuilles, une poignée de cellules clés et la présence des graphiques suffisent comme contrôle rapide par profil d'export
Trier un fichier ODS avant de s'engager dans un import
Quand un point d'entrée accepte des dépôts, lister les noms de feuilles coûte bien moins cher qu'une analyse complète et attrape tôt les surprises structurelles :
Names := TStringList.Create;
Book := TXLSXWorkbook.Create;
try
if Book.GetODSSheetNames('incoming.ods', Names) <= 0 then
raise Exception.Create('not a readable ODS package');
if Names.IndexOf('Data') < 0 then
raise Exception.Create('revision is missing the Data sheet');
finally
Book.Free;
Names.Free;
end;
La convention de retour fait trébucher : les appels HotXLS renvoient généralement un compte positif ou 1 en cas de succès et -1 en cas d'échec, en vidant la liste lorsqu'ils échouent, alors testez <= 0 plutôt que de comparer à une valeur positive précise. GetODSSheetNames ne réinitialise ni ne remplit l'instance de classeur, si bien qu'un seul objet sonde peut examiner tout un répertoire de fichiers entrants. Les contrôles structurels de ce genre attrapent la panne la plus courante en conditions réelles — un analyste qui renomme ou supprime une feuille avant de renvoyer la révision — au portail, là où le message d'erreur peut encore nommer le fichier et la feuille manquante au lieu de ressortir en référence nulle trois couches plus bas
Si vous bâtissez un pipeline de conversion plus large autour de cela, le motif de l'atelier d'audit et de conversion de classeurs montre comment inventorier les fonctionnalités d'un fichier avant de choisir un format cible, et le guide de performance des grands classeurs garde les exports par lots dans des bornes mémoire raisonnables
HotXLS est une bibliothèque tableur native pour Delphi et C++Builder livrée avec le code source complet ; la liste exhaustive des fonctionnalités et les modalités de licence figurent sur la page produit du HotXLS Delphi Component