David Madore's WebLog: Déboires avec OpenOffice.org, ou comment mettre en page un roman

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]

↓Entry #1564 [older| permalink|newer] / ↓Entrée #1564 [précédente| permalien|suivante] ↓

(lundi)

Déboires avec OpenOffice.org, ou comment mettre en page un roman

Je laisse (temporairement ?) de côté la suite de mes rants contre le principe de précaution, parce que ça va sans doute me prendre encore beaucoup trop de temps d'écrire les numéros (2) et (3).

Mes déboires avec OpenOffice.org ont commencé lorsque mon poussinet, au cours d'une discussion sur des romans d'un auteur de heroic fantasy que nous apprécions tous deux (Raymond Feist), a appris que j'avais moi-même écrit un roman quand j'étais petit (roman qui est plus cher à mon cœur qu'il n'est littérairement potable), et il [le poussinet] a exprimé le désir[#] de le lire. Seulement, pour ça, il en fallait une version imprimée, la version HTML étant trop peu commode à lire (version HTML qui actuellement est mais je compte la remplacer par mieux, justement, cf. ci-dessous). D'où l'idée, née de mon enthousiasme naïf et de la supposition candide que la technique marche parfois, d'en générer une version PDF par conversion d'une version OpenDocument via OpenOffice.org. [Ajout : voir cette entrée ultérieure au sujet de ce roman,]

Quelques explications sur les formats s'imposent peut-être.

Le format PDF est un format de description de document déjà mis en page mais vectoriel (vectoriel signifiant qu'il ne suppose pas une résolution particulière mais se décrit en termes de primitives graphiques générales comme des courbes de Bézier, ce qui permet de zoomer autant qu'on veut sans effet désagréable de pixelisation) : on peut s'imaginer qu'il contient des descriptions de texte dans différentes polices (les polices en question, a priori vectorielles, pouvant être embarquées dans le document ou supposées connues du lecteur) ou des commandes de dessin vectoriel. Un format de document mis en page suppose un format de papier bien défini (A4, B5, US legal, que sais-je encore), et a priori on ne peut pas en changer (sauf à appliquer un zoom ou à recentrer le texte dans la page), en tout cas on ne peut pas repaginer le document.

À l'opposé de tels formats mis en page, on a des formats sémantiques, dont un exemple serait le DocBook, et qui consistent à décrire le texte d'un document non par sa position dans la page mais par sa fonction sémantique (telle que : titre de chapitre, note en bas de page, citation en exergue, etc.). Pour passer d'un format sémantique à un format mis en page (et bon à imprimer), on va créer une feuille de style qui va indiquer la façon dont on veut mettre en page les différentes fonctions sémantiques reconnues dans le document (i.e., la police à utiliser pour le titre, la taille des notes en bas de page, les marges des citations en exergue) ; le contenu sémantique et la feuille de style sont normalement gardés bien séparés (principe de séparation du fond et de la forme), et un programme de mise en page va prendre les deux, les passer à la moulinette et produire un PDF (ou tout autre format mis en page).

Enfin, ça c'est la théorie. La pratique, c'est que rien n'est parfait. Le PDF est un format qui a parfois des problèmes incompréhensible (voir plus loin), et les formats sémantiques on ne sait pas très bien où les trouver ; la séparation fond/forme est un truc complètement théorique et qui marche très mal sur des documents réels, notamment parce qu'un livre réel (sauf peut-être s'il s'agit d'une documentation technique) va contenir beaucoup de choses qui se placent dans une région bien floue entre le fond et la forme (du genre : tel paragraphe est centré parce que l'auteur a décidé de le centrer — on ne sait pas bien quelle est la sémantique profonde derrière, etc.), du coup, on multiplie les classes ou les styles de texte, et on finit par leur donner des noms comme centré ou aligné à droite, et à ce stade la distinction fond/forme part aux chiottes.

Quelque part entre les formats mis en page et les formats sémantiques, on a le format OpenDocument, qu'on pourrait décrire comme le format natif de OpenOffice.org (depuis la version 2) mais qui est surtout l'alternative ouverte au format de Microsoft Word. Un fichier au format OpenDocument est une archive (zip) contenant un certain nombre de fichiers, principalement XML, dont l'un (content.xml) contient censément le fond et un autre (style.xml) est la feuille de style qui contrôle la forme (les autres fichiers de l'archive contenant, par exemple, les méta-données du document, les images, les macros, etc.).

L'idée que j'avais, et qui était parfaite en théorie, était la suivante : retravailler un peu le source du livre pour en avoir une version XML propre selon un schéma personnel[#2] à la sémantique aussi claire que possible, puis avoir des scripts qui pourraient convertir ce XML source soit en un format (X)HTML pour l'affichage comme page Web soit en un format OpenDocument qu'OpenOffice.org pourrait ensuite paginer et transformer en PDF. Ainsi, il me serait possible d'avoir simultanément une version HTML et une version PDF, les deux découlant d'un même source (donc si je corrige une faute d'orthographe dans le source, il n'est pas trop pénible de régénérer les deux versions publiées), et les deux étant de bien meilleure qualité que si je produisais le PDF directement à partir du HTML (ce qui est systématiquement épouvantable et ce qui interdit quelque chose comme une belle table des matières). Voilà, ça c'était la théorie. Comme d'habitude, il n'y a pas de différence entre la théorie et la pratique — en théorie.

Le roman était à l'origine écrit sur le traitement de texte Sprint de Borland (sous MS-DOS). Différentes manipulations de format l'avaient transformé en TeX, puis en HTML. Je n'ai pas eu beaucoup de mal, à l'aide de recherches-remplacements dans un quelconque éditeur, à sortir un fichier XML que je pouvais considérer comme un source primaire à utiliser désormais.

Première difficulté : comment écrire les scripts de transformation ? S'agissant de transformer un fichier XML (source) en un autre fichier XML (le content.xml du fichier OpenDocument), il y a un langage a priori fait pour ça : XSLT ; mais XSLT est un langage de programmation très particulier, qui n'a pas vraiment de variables ni de boucles au sens où on l'entend habituellement, et qui, s'il facilite certaines opérations, en rend d'autres absolument atroces, et notamment pour ce qu'on appelle la fabrication des styles automatiques OpenDocument, j'ai vite compris qu'utiliser XSLT tournerait à la séance de SM cuir et chaînes. Donc je me suis rabattu sur le langage général Perl et sa bibliothèque XML::LibXML pour manipuler du XML ; c'est certainement plus puissant que XSLT, mais c'est aussi souvent inutilement verbeux, par exemple quand je vois la quantité d'incantations propitiatoires que je dois prononcer pour émettre trois malheureuses lignes de XML définissant les polices du document :

$tgt_root->appendChild($tgt->createTextNode("\n"));
$tgt_root->appendChild($tgt->createComment(" Font declarations "));
$tgt_root->appendChild($tgt->createTextNode("\n"));
my $font_face_decls = $tgt->createElementNS($nspfx{"office"},"office:font-face-decls");
$tgt_root->appendChild($font_face_decls);
$font_face_decls->appendChild($tgt->createTextNode("\n"));
sub add_font_decl {
    my $font = $tgt->createElementNS($nspfx{"style"},"style:font-face");
    $font_face_decls->appendChild($font);
    $font_face_decls->appendChild($tgt->createTextNode("\n"));
    $font->setAttributeNS($nspfx{"style"},"style:name",$_[0]);
    my $qname = $_[0];
    $qname = "'" . $qname . "'" if $qname =~ / /;
    $font->setAttributeNS($nspfx{"svg"},"svg:font-family",$qname);
    $font->setAttributeNS($nspfx{"style"},"style:font-adornments",$_[1]);
    $font->setAttributeNS($nspfx{"style"},"style:font-family-generic",$_[2]);
    $font->setAttributeNS($nspfx{"style"},"style:font-pitch",$_[3]);
}
add_font_decl "DejaVu Serif", "Book", "roman", "variable";
add_font_decl "DejaVu Sans", "Book", "swiss", "variable";
add_font_decl "Linux Libertine", "Book", "roman", "variable";
$tgt_root->appendChild($tgt->createTextNode("\n"));

…beurk ! Comme d'habitude, le diable est dans les détails : le principe général du code est facile à écrire (quand on lit un <p> dans le source, on émet un <text:p> à la sortie), mais plein de petits problèmes viennent se greffer dessus et transformer le programme en une tambouille illisible (pour générer le titre du livre tout entier, que je veux centrer sur une page, il me faut émettre un <text:p> contenant un <draw:frame> contenant un <draw:text-box> contenant un <text:p>, et tout d'un coup je me rends compte que certaines opérations appliquées dans mon programme à un <text:p> doivent ici s'appliquer au <text:p> intérieur et d'autres au <text:p> extérieur et que je dois tout d'un coup les séparer — et hop, beaucoup de lignes de code en plus).

Autre aspect pénible : la feuille de style (le fichier style.xml du OpenDocument), que j'écris à la main parce que c'est tout de même plus commode que de rajouter de la sauce Perl autour, se met à contenir des choses que je voudrais bien générer automatiquement (par exemple, le format OpenDocument ne permet pas de contenir le plus simple calcul pour les marges : donc si je veux du format papier B5 au lieu de A4 il faut que je recalcule plein de choses).

Ensuite, on commence à tomber sur les bugs de OpenOffice (je ne parle même pas des autres programmes censés pouvoir lire du OpenDocument, tels que kword, abiword et okular : soit ils plantent carrément en lisant mon fichier, soit ils en font de la bouillie pour le formatage). Il y en a un qui m'a beaucoup énervé, qui concerne la table des matières : normalement, le format OpenDocument prévoit que le document peut contenir des balises <text:h> pour délimiter les divisions (chapitres, sous-chapitres, sections, sous-sections, etc.) à faire figurer dans la table des matières, chacune étant accompagnée d'un attribut numérique text:outline-level qui indique le niveau d'imbrication (et typiquement, d'indentation dans la table des matières) ; la balise peut aussi indiquer un style, qui n'a a priori rien à voir avec le niveau d'imbrication (par exemple, au même niveau d'imbrication, j'ai des titres de chapitre et des balises de titres d'appendice) ; mais OpenOffice a un bug qui fait qu'il traite ça n'importe comment : il prend le premier style rencontré pour chaque niveau d'imbrication et décide que ce style (et aucun autre) « est » ce niveau d'imbrication et ne fait figurer que les paragraphes ayant ce style dans la table des matières. Pas de moyen simple, donc, d'avoir une table des matières qui fera figurer à même niveau d'imbrication les styles titre de chapitre et titre d'appendice. Ah si, on peut créer la table des matières à partir de styles additionnels, ce qui fait ce que je veux (au prix de la clarté sémantique du document), mais alors on tombe sur un autre bug qui fait qu'on n'aura pas d'hyperliens dans la table des matières ! Je me frappe la tête contre les murs : adieu l'idée d'obtenir un OpenDocument propre et clair. Bon, j'ai fini par trouver une façon de plus ou moins satisfaisante de contourner le problème, mais que de temps perdu à comprendre ce qui ne va pas, à soumettre des bug-reports[#3], à décider de la moins mauvaise façon de contourner le problème et ainsi de suite !

Autre limitation horripilante de OpenOffice : impossible de trouver un format vectoriel dans lequel je puisse inclure les cartes censées illustrer le roman. En effet, OOo ne comprend pas le format vectoriel SVG (et une limitation fondamentale, l'absence de clipping dans le format de dessin vectoriel natif OpenDocument, fait qu'aucun convertisseur n'a de chance de pouvoir marcher), et il ne sait pas non plus inclure d'images PDF. Reste le format EPS, qu'il arrive plus ou moins à inclure et auquel j'ai pu convertir mes cartes[#4], mais d'une part il prend un temps fou à les afficher (parce qu'il appelle un programme externe qui, pour une raison qui m'échappe, est infiniment lent dans ce cas), et d'autre part, si on demande d'exporter le fichier en PDF, les images en question sont alors rastérisées (c'est-à-dire qu'elles cessent d'être vectorielles pour devenir des tableaux de pixels à une résolution donnée) ; en revanche, bizarrement, si on imprime vers une imprimante PostScript, les images en question restent vectorielles, et on peut convertir ce fichier PostScript en PDF… mais on perd les liens de la table des matières ! D'où la seule solution que j'ai trouvée : produire deux fichiers PDF, l'un par exportation depuis OpenOffice, l'autre en imprimant en PostScript et en convertissant ensuite en PDF, puis utiliser un programme (pdftk) pour mélanger les pages de l'un et l'autre fichier PDF afin d'avoir, au final, à la fois les cartes au format vectoriel et les liens qui marchent depuis la table des matières. Quelle horreur !

Encore un souci que j'ai rencontré : comment faire générer la table des matières dès l'ouverture du document ? En effet, le fichier OpenDocument que je produis n'est pas paginé (et pour cause, je compte justement sur OpenOffice pour faire la mise en page), c'est-à-dire qu'il ne contient ni sauts de ligne doux ni sauts de page doux (le mot douxsoft — signifiant que les sauts en question ont été calculés automatiquement et pas imposés par la structure du document : la grande majorité des saux sont doux, les sauts durs viennent avant un changement de chapitre pour ce qui est des sauts de pages ou à la fin d'un vers pour les sauts de ligne, bref, ce genre de choses). Du coup, la table des matières ne peut pas contenir de numéros de pages, il faut la faire recalculer à OpenOffice pour qu'elle en contienne. J'ai donc dû écrire une macro en basic OOo, qui s'exécute à l'ouverture du document (en déclanchant toutes sortes d'alarmes de sécurité, bien sûr, vu que le basic en question n'est pas sécurisé), qui déclenche la réévaluation de la table des matières. Il est vrai que, là, je m'attendais à avoir plus de mal que ça, et que finalement ça a bien marché.

Mais ce n'est toujours pas la fin de mes ennuis : ensuite, il y a eu celui du choix de la police (il est vrai qu'OpenOffice n'a plus rien à voir là-dedans). J'aurais bien pris une des grandes polices traditionnelles (disons, [New] Century Schoolbook, qui me semble assez appropriée au caractère un peu enfantin du roman, mêlée à Univers ou Helvetica pour les titres et Optima pour la préface). Malheureusement, on connaît mon habitude de faire joujou avec Unicode : il a fallu que je misse des citations en grec et en russe dans le roman, et je n'ai pas de version grecque ou cyrillique des polices que je viens de citer (j'ignore même si quelqu'un en a fait une). Autre possibilité : Gentium, une police libre moderne dont j'apprécie l'élégance sobre et reserrée, et qui contient le grec polytonique et à peu près le cyrillique. Malheureusement, ce n'est qu'à peu près, puisque j'ai fini par me rendre compte qu'il me manquait encore quelques caractères (le cyrillique est très incomplet dans la version italique de la police). Finalement, je me suis rabattu sur Linux Libertine, une police libre très complète (et qui malgré son nom n'a absolument aucun rapport avec Linux), assez proche des classiques Baskerville et Janson, et pour les titres et la préface j'ai pris Vera Sans et Vera Serif respectivement (dans leur version DejaVu pour recouvrir plus de caractères Unicode).

Bon, l'un dans l'autre, après ces durs labeurs, j'ai réussi à obtenir ce fichier OpenDocument et ce fichier PDF. Pour l'imprimer, je n'ai pas pensé à des sites spécialisés d'impression sur demande comme Lulu.com[#5] (je croyais que ça n'existait qu'à partir d'une centaine d'exemplaires : là j'en voulais seulement un ou deux), du coup je suis bêtement allé dans une boutique de reprographie et d'impression numérique. Et là, les imprimantes+photocopieuses Canon, auxquelles l'impression a été confiée, n'ont pas du tout apprécié mon PDF : elles ont mis quelque chose comme trente secondes par page (du coup je suis revenu chercher le résultat le lendemain), sans évidemment la moindre explication sur ce qui prenait tellement de temps à imprimer. Du coup, beaucoup du bien que je pensais du format PDF est parti (mais je ne sais pas si la faute doit être imputée aux drivers Canon, à OpenOffice, ou à la façon un peu compliquée dont j'étais obligé de produire le PDF comme je l'ai expliqué plus haut).

Bon, maintenant, il ne me reste qu'à refaire un fichier HTML à partir du nouveau XML source que j'ai.

[#] À moins que ce soit pour me faire plaisir ? Mais feignons d'ignorer cette éventualité.

[#2] Le XML est un format complètement général qui permet de représenter tout et n'importe quoi comme donnée structurée (disons que c'est juste une façon de sérialiser un arbre abstrait sous forme de données textuelles) : pour donner un sens à du XML il faut décider du sens qu'on va donner aux différentes balises et comment on va autoriser à les emboîter (le schéma).

[#3] Soumettre un bug-report à un programme est souvent très long car non seulement on doit chercher à produire un fichier simple et clair qui illustre le bug, mais en plus il faut s'inscrire dans l'outil de rapport de bug, ce qui demande d'attendre un mail de confirmation de mot de passe, etc.

[#4] Non sans mal ! Je ne sais pas pourquoi, pour partir du fichier PS que j'avais, j'ai dû convertir en PDF, reconvertir le PDF en PS, et convertir ce PS-là en EPS pour avoir quelque chose que OpenOffice accepte de digérer.

[#5] Une utilité que j'aurais pu y trouver aurait été d'imprimer en B5 (j'aime bien le format B5, pour les livres). Las ! ils ne le proposent pas.

↑Entry #1564 [older| permalink|newer] / ↑Entrée #1564 [précédente| permalien|suivante] ↑

[Index of all entries / Index de toutes les entréesLatest entries / Dernières entréesXML (RSS 1.0) • Recent comments / Commentaires récents]