Pourquoi les formats de fichiers de Microsoft Office sont si compliqués ? (Et quelques moyens de contourner les problèmes)

Cet article est apparu sur la page principale de Joel on Software le mardi 19 février 2008.

La semaine dernière Microsoft a publié le format des fichiers binaires d’Office. Ces formats apparaissent à première vue à la limite de l’insulte. Rien que le format de fichier pour Excel 97-2003 est un fichier PDF de 349 pages ! Attendez, ce n’est pas tout ! Il y a, dans ce document, quelques commentaires intéréssants :

Chaque collection de tableaux Excel (workbook) est stocké dans un fichier qui est une « compilation d’objets ».

Oui, vous voyez, les fichiers Excel 97-2003 sont des « compositions » de documents OLE, qui sont principalement plusieurs fichiers réunis en un seul. C’est tellement compliqué qu’il vous faut lire au minimum 9 pages de spécifications pour réussir à comprendre ça. Et ces « specs » ressemblent plus à des structures en C (« struct« ) que ce à quoi on s’attend normalement quand on lit des spécifications. C’est en réalité un système complet hiérarchique de fichiers.

Si vous avez commencé à lire ces documents avec l’espoir de passer le week-end à écrire rapidos un code qui importe des documents Word dans votre système de blog, ou qui crée des tableaux Internet qui contiennent vos données de comptes bancaires, formattés comme les feuilles Excel de votre suivi personnel, la complexité et la longueur des spécifications vous ont très certainement guéri de ce violent désir. Et vraisemblablement très rapidement. D’ailleurs un développeur normal concluerait que les formats binaires des applications Office :

  • sont volontairement obscurcis ;
  • sont le produit d’un cerveau démentiel ;
  • ont été crée par des développeurs anormalement incompétents ;
  • et sont impossibles à lire ou écrire correctement.

Vous auriez tort sur tous les points. En creusant un peu, je vais vous montrer comment ces formats de fichier sont devenus incroyablement compliqués, pourquoi cela ne reflète pas l’idée qu’on imagine de la « mauvaise programmation made in Microsoft », et ce que vous pouvez faire pour contourner ces problèmes.

La première chose à intégrer est que les formats binaires ont été conçus avec des objectifs complètements différents de ce qu’on imagine aujourd’hui (HTML). Voici ces objectifs, vous allez ainsi mieux comprendre pourquoi les spécifications sont aussi longues :

  • Ils ont été spécifiquement conçus pour être rapides sur les vieux ordinateurs. Dans les premières versions d’Excel pour Windows, 1 méga-octets de RAM était une taille normale de mémoire vive, et un PC 80386 à 20 MHz devait faire fonctionner Excel de manière confortable. Il y a donc plein d’optimisations dans les formats de ces fichiers qui ont pour objectif de les ouvrir et de les fermer beaucoup plus rapidement.
  • Ce sont des formats binaires, donc charger un enregistrement n’est qu’une question de copier directement (« blit ») une séquence d’octets, du disque dur vers la mémoire vive, où vous finissez immédiatement avec un structure de données de type C (« struct« ) qui est utilisable. Il n’y a pas besoin d’analyser ou de parcourir quoi que ce soit lors du chargement. L’analyse syntaxique et le parcours d’un enregistrement sont incomparablement, énormément, atrocement plus longs qu’un simple « blit ».
  • Le format de fichier est un peu tordu là où c’était nécessaire, là où on fait toutes les opérations habituelles, et cela à des fins de rapidité. Par exemple, Excel 95 et 97 ont quelque chose appelé « Sauvegarde Simple » qu’ils utilisent parfois parce que c’est une variante plus rapide sur une partie spécifique du format OLE, parce que dans les cas de sauvegarde normales, ce n’était pas assez rapide. Word a un menu appelé « Sauvegarde Rapide ». Pour sauver rapidement un long document, 14 fois sur 15, seuls les changement sont ajoutés en fin de fichier, au lieu de réécrire entièrement le document en partant de zéro. Sur les disques durs de l’époque, cela se traduisait par le fait qu’un gros document était sauvé en une seconde au lieu de trente. (Cela signifiait aussi que les données effacées dans un document ne l’étaient pas vraiment et étaient toujours présentes dans le fichier. Ce que beaucoup de gens par la suite n’ont pas apprécié).
  • Ils étaient crées dans le but de pouvoir utiliser des librairies. Si vous voulez écrire un « importateur » binaire, vous devez prendre en compte des choses telles que le format de métafichiers Windows (« Windows Metafile Format ») (pour dessiner des formes) et la gestion d’objets OLE embarqués (« OLE Compound Storage »). Si vous êtes sous Windows, il y a une librairie qui gère déjà tout ça et rend les choses triviales…. d’ailleurs, s’en servir a procuré un bon gain de temps à l’équipe de Microsoft. Mais si vous faites tout du début à la fin, vous devrez tout faire vous-même.Office s’accomode parfaitement avec tout ce qui est documents embarqués, par exemple, vous pouvez embarquer une feuille Excel dans un document Word. Un déchiffreur parfait de format Word devrait donc aussi être capable de faire quelque chose avec cette feuille embarquée.
  • Ils n’ont pas été crées avec le mot « interopérabilité » en tête. La supposition à l’époque, et c’était relativement raisonnable de penser ainsi, était que le format Word ne devait être lu et écrit que par Word lui-même. Cela impliquait que lorsqu’un développeur de l’équipe de Word devait prendre une décision sur des changements dans le format du fichier, la seule chose à laquelle il devait faire attention était que ces changements soient (a) rapides à lire et écrire et (b) prennent le moins de code possible dans le programme exécutable Word. Les choses telles que des formats standardisés SGML et HTML, n’ont jamais été pris en compte jusqu’à ce qu’Internet rende indispensable le fait de pouvoir échanger des documents qui soient compatibles entre eux. Et cela, c’est une dizaine d’années après que le format binaire de Microsoft Office n’ait été inventé. De toute façon, il était possible d’utiliser des « importeurs/exporteurs » pour s’échanger des documents. En réalité, Word a vraiment un format crée spécifiquement pour des exports destinés à d’autres programmes, appelé RTF, qui existe d’ailleurs depuis presque le début, et qui est toujours supporté à 100%.
  • Ils devaient pouvoir intégrer toute la complexité du logiciel. Chaque boîte à chocher (« checkbox »), chaque option de formattage, bref, toutes les possibilités offertes par Microsoft Office doivent être représentées quelque part dans les formats de fichier. Cette boîte à chocher (« checkbox »), dans le menu de Word appelée (« Paragraphe solidaire ») qui force un paragraphe à être automatiquement déplacé sur la page suivante si nécessaire afin de rester associé avec le paragraphe qui le suit ? Cela doit être présent dans le format du fichier.Et cela veut dire que si vous voulez développer un clone parfait de Word, qui peut lire correctement les documents Word, vous devez implémenter cette possibilité. Si vous voulez créer un programme destiné à écrire, qui soit compétitif, et qui a la possibilité de charger des documents Word, cela peut vous prendre une minute pour écrire le code qui va charger ce bit dans le fichier proprement dit, mais cela va certainement vous prendre des semaines à réussir à transformer tout l’algorithme de votre affichage pour que ce dernier s’adapte à ce fameux bit. Si vous ne le faites pas, les clients qui voudront ouvrir leurs documents Word dans votre clone vont voir toutes les pages sens dessus-dessous.
  • Ils ne peuvent que refléter l’histoire des applications. La plupart des choses compliquées dans ces formats de fichiers concernent des possibilités ou des options qui sont vieilles, compliquées, pas appréciées et rarement utilisées. Elles sont toujours là par souci de compatibilité, et parce que cela ne coûte rien à Microsoft de laisser du code. Mais si vous voulez vraiment faire un boulot complet et dans le moindre détail, et que vous voulez lire et écrire dans ces formats, vous allez devoir aussi faire le travail qu’un interne de chez Microsoft a fait il y a 15 ans en arrière. Le pire de tout c’est qu’il y a des centaines d’années-homme de développement qui se trouvent à l’intérieur de Word de d’Excel, et si vous voulez clôner ces applications complètement, vous allez devoir faire ces centaines d’années de travail. Un format de fichier et juste un résumé très concis de toutes les possibilités qu’offre une application.Juste pour le fun, voyons un exemple minuscule en détail. Un onglet Excel est un paquet d’enregistrements BIFF de types différents. Je ne vais parler que du premier enregistrement BIFF des spécifications. C’est un enregistrement appelé 1904.

    Les spécifications du format de fichier Excel sont remarquablement obscures sur cet enregistrement. Elles précisent juste que l’enregistrement 1904 indique « si le système de date 1904 est utilisé ». Ah. Super. Un exemple classique de spécification complètement inutilisable. si vous être un développeur qui travaille sur le format Excel, et que vous lisez ça dans les spécifications, vous pourriez tout à fait en conclure que Microsoft essaie de cacher quelque chose. En effet, vous n’avez pas assez d’information. Il vous faut plus de connaissances pour comprendre tout cela, mais je suis gentil et je vais vous l’expliquer. Il y a deux types de feuillets Excel : ceux qui se basent sur la date de base qui est le 1/1/1900 (avec un bogue d’années bissextiles volontairement crée afin d’être compatible avec Lotus  1-2-3, mais c’est trop ennuyeux pour en parler ici), et ceux dont la date de base est le 1/1/1904. Excel supporte ces deux types de dates parce que la première version d’Excel pour Mac se servait de la date de base du système d’exploitation (c’était le plus facile), mais Excel pour Windows devait impérativement pouvoir importer les fichiers Lotus 1-2-3, qui se servaient de la date de base au 1/1/1900. Déjà rien que ça devrait vous faire pleurer de consternation. Tout au long de cet histoire affligeante, pas un seul développeur n’a pensé à faire quelque chose de correct, mais bon voilà : vous avez la chose ici, et il vous faut faire avec.Vous trouverez vraiment ces deux types de fichiers, 1900 et 1904, un peu partout, selon que le fichier soit originaire de Windows ou de Mac. Les convertir de manière silencieuse peut éventuellement générer des problème d’intégrité des données, donc Excel ne changera jamais le type pour vous. Si vous voulez parcourir des fichiers Excel il faut faudra savoir gérer les deux. Ce n’est pas juste une simple question de charger ce petit bit à partir du fichier. Cela veut dire aussi que vous allez devoir réécrire entièrement tout l’affichage des dates avec un paramètre supplémentaire pour pouvoir prendre en compte ces deux types de date de base (epochs). Comptez au minimum plusieurs jours.

    Bien évidemment, lorsque vous travaillerez sur votre clone Excel, vous découvrirez plein de petits détails subtils sur la gestion des dates. A votre avis, quand est-ce qu’Excel fait la conversion de nombres en dates ? Comment fonctionne le formatage ? Pourquoi est-ce que « 31/1 » est interprété comme le 31 janvier de l’année en cours, alors que « 1/50 » est considéré comme le premier janvier de l’année 1950 ? Toute ces petites choses très subtiles qui concernent les comportements ne peuvent pas être complètement documentées à moins d’écrire un document pratiquement aussi long qui le code source d’Excel lui-même.Et c’est juste le premier enregistrement BIFF, alors qu’il y en a plusieurs centaines que vous devrez gérer. Et pour ne rien vous cacher : celui-ci est l’un des plus simples. La plupart des BIFF feront pleurer de rage un développeur expérimenté.

Une seule conclusion s’impose : c’est très gentil à Microsoft de fournir officiellement un document décrivant le format de leurs fichiers Office, mais ça ne va pas pour autant vous rendre la vie super facile si vous décidez d’écrire quelque chose qui importera ou sauvera sous le format Office. Ces applications sont affreusement complexes et subtiles, pleines de possibilités et vous ne pouvez pas simplement implémenter 20 % des fonctions les plus populaire et espérer que 80 % de votre clientèle sera satisfaire. La spécification du format binaire vous aidera à gagner quelques minutes, tout au plus quelques jours, et vous évitera de faire du reverse engineering. Rien de plus.

OK, je vous ai promis quelques solutions de contournement. La bonne nouvelle c’est que pour la plupart des applications qui veulent lire ou écrire au format Office ne font pas un bon choix. Il y a deux alternatives qu’il vous faut immédiatement étudier : soit laisser Office faire ce travail, soit écrire dans un format plus facile (ce qui ne veut pas dire moins puissant).

Laissez Office faire le boulot pour vous. Word et Excel ont des modèles objet extrêmement complets, disponibles via l’automation COM, qui vous donne la possibilité de faire tout par programmation. Dans plein de cas il est beaucoup plus pertinent de réutiliser le code dans Office plutôt que d’essayer de le ré-implémenter. Voici quelques exemples.

  1. Vous avez une application Web qui doit transformer des documents Word en PDF. Voici comment je le ferais : quelque lignes de VBA Word pour charger le fichier et le sauver au format PDF en utilisant le convertisseur PDF intégré de Word 2007. Vous pouvez appeler ce code directement, même en ASP ou ASP.NET sous IIS. Cela fonctionnera. La première fois, il y aura un chargement de Word et ça prendra quelques secondes. Les fois suivantes, Word sera gardé en mémoire par le sous-système COM pendant quelques minutes au cas où vous en auriez besoin. C’est suffisamment rapide pour n’importe quelle application Web de taille moyenne.
  2. Même chose que précédemment, mais vous êtes hébergé sur du Linux. Achetez un serveur Windows 2003, installez une copie de Word avec une licence pro, et mettez en place un petit Webservice qui fait ce travail. Une demi journée de travail avec C# et ASP.NET.
  3. Même chose que précédemment, mais il vous faut être plus réactif : votre application a grossi. Mettez un « load balancer » en face de plusieurs PC que vous aurez mis en place de la même manière qu’à l’étape précédente. Aucun code supplémentaire.

Ce type d’approche pourrait fonctionner si vous avez pour objectif de faire dans vos applications des appels simples et communs à des objets Office. Par exemple :

  • Ouvrir une fiche  Excel, mettre des informations dans les cellules, recalculer, et extraire des résultat des cellules ;
  • Utiliser Excel pour générer des diagrammes au format GIF ;
  • Extraire presque n’importe quel type d’information de n’importa quelle feuille Excel sans perdre une seconde à réfléchir au format de fichier qu’il y a derrière ;
  • Convertir un fichier Excel au format CSV tabulaire (une autre méthode pratique serait d’utiliser le drive ODBC d’Excel pour aspirer suck data out using SQL queries).
  • Editer des deocuments Word ;
  • Remplir des fiches Word ;
  • Convertir des fichiers entre tous les formats supportés par Office (il y a plein d' »importateurs » qui savent lire les formats de programmes de traitement de texte et de feuilles de calcul).

Dans tous les cas, il y a des façons de dire aux objets Office qu’il ne sont pas en dans une applications interactive, et qu’ils ne doivent pas s’occuper de rafraîchir l’écran et qu’ils ne doivent en aucun cas essayer d’ouvrir une boite de dialogue. Au fait, si vous choisissez cette solution, il y a quelques petites astuces et particularités qu’il faut bien avoir en tête, qui ne sont pas officiellement supportées par Microsoft. Parcourez la base de connaissance de Microsoft sur le sujet avant d’aller plus loin.

Utilisez un format de fichier plus simple pour écrire des fichiers. Si vous devez générer des documents Office par la programmation, il y a presque toujours une façon plus facile et plus pratique que le format binaire Office, que vous pouvez utilisez, et que Word et Excel ouvriront tout de même sans problème, le tout sans rien perdre de vos objectifs.

  • Si vous devez tout simplement générer des données tabulaires pour les ressortir dans Excel, utilisez CSV ;
  • Si vous avez absolument besoin de formules de calcul que CSV ne supporte pas, le format WK1 (Lotus 1-2-3) est incroyablement plus simple que celui d’Excel, et Excel saura le lire ;
  • Si vous devez vraiment, absolument, générer des fichiers natifs Excel, commencez par une très vieille version d’Excel… Excel 3.0 est un bon compromis (c’est le format juste avant que tout ces trucs imbriqués dans des trucs imbriqués n’apparaissent), et sauvez un fichier minimal qui contient uniquement les choses dont vous voulez vous servir. Utilisez ce fichier pour voir le nombre minimal d’enregistrements BIFF que vous devrez ressortir et concentrez vous, dans les spécifications, uniquement sur ces parties ;
  • Pour des documents Word, pensez au format HTML. Word les ouvrira de manière parfaitement transparente.
  • Si vous voulez vraiment générer des documents Word avec des choses particulière et originales dedans, le meilleur moyen sera presque toujours de générer un fichier RTF. Tout ce que Word peut faire peut être écrit dans un fichier RTF, et ce format est un format text simple, pas binaire, et vous pouvez même changer à la main des choses dans le fichier, et Word l’ouvrira toujours certainement sans problème. Vous pouvez créer un document sympa formatté comme vous le désirez, avec des mots-clé que vous voulez remplacer, vous demandez ensuite à Word de sauver le document au format RTF et vous remplacez à la main (ou par programmation) directement dans le fichier RTF ces champs avec les mots-clé à la volée. Et ce document, vous pourrez l’ouvrir sous Word sans aucun problème.

Pour conclure, à moins que vous ne vouliez créer un concurrent d’Office qui peut lire et écrire tous les fichiers Office parfaitement, auquel cas vous aurez des centaines d’années de travail devant vous, il y a de fortes chances que, quel que soit le problème que vous vouliez résoudre, le plus gros de votre travail soit de lire et d’écrire des fichiers Office.

Poster un commentaire

Vous devriez utiliser le HTML:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>