Retrouvez cet article dans : Linux Magazine 104
# aptitude install python-docutilsChez vous, je ne sais pas, mais, en tout état de cause, la procédure d’installation [1] est parfaitement documentée.
Plantons le décor
Un peu d’histoire
Le traditionnel fichierComprendre la structure de Docutils
L’ensemble Docutils est donc une chaîne d’outils qui va prendre une source (le plus souvent un fichier texte) en amont et produire en aval une sortie formatée dans la forme choisie, LaTeX ou HTML par exemple. Chaque type d’outil a sa responsabilité (on retrouve un peu la philosophie " faire une chose et la faire bien ") dans l’ensemble. On distingue :$ cd /usr/share/pycentral/python-docutils/site-packages/docutils $ ls –l total 260 -rw-r--r-- 1 root root 26741 2007-11-18 11:59 core.py -rw-r--r-- 1 root root 3858 2005-06-27 13:19 examples.py -rw-r--r-- 1 root root 32086 2005-12-12 05:12 frontend.py -rw-r--r-- 1 root root 6711 2007-11-18 11:59 __init__.py -rw-r--r-- 1 root root 11570 2005-07-03 17:02 io.py drwxr-xr-x 2 root root 4096 2007-11-19 08:23 languages -rw-r--r-- 1 root root 58983 2007-11-18 11:59 nodes.py drwxr-xr-x 3 root root 4096 2007-11-19 08:23 parsers drwxr-xr-x 3 root root 4096 2007-11-19 08:23 readers -rw-r--r-- 1 root root 55377 2005-12-08 00:46 statemachine.py drwxr-xr-x 2 root root 4096 2007-11-19 08:23 transforms -rw-r--r-- 1 root root 6321 2005-12-30 04:00 urischemes.py -rw-r--r-- 1 root root 20952 2007-11-18 11:59 utils.py drwxr-xr-x 7 root root 4096 2007-11-19 08:23 writersOn note bien la présence des répertoires
Les notions importantes de reStructuredText
Encore une fois, on ne va pas expliquer toute la syntaxe de reST, mais plutôt en montrer le principe, et surtout voir les deux briques les plus importantes de l’édifice, le texte interprété, et les directives. Un document reST est constitué de blocs (paragraphe, liste, table...). La syntaxe permet de définir le type de bloc, de titrer les sections, mais également de marquer certaines parties de texte à l’intérieur des blocs (emphase, hyperliens, appels de notes...). On parlera alors de marquage en ligne. Le texte interprété fait partie du marquage en ligne, les directives sont (ou marquent) des blocs.Le texte interprété
La documentation de référence [6] de reST précise qu’il s’agit d’une partie de texte qui est destinée à être indexée, liée, résumée ou tout autre type de traitement, mais en ne touchant généralement pas le texte lui-même. Ainsi, une entrée d’index simple ne sera pas " marquée " dans le corps du texte et apparaîtra telle quelle : elle fera en revanche l’objet d’un traitement de façon à apparaître dans l’index du document. La marque du texte interprété est le backtick (accent grave), précédé ou suivi du rôle que devra jouer le texte en question, une référence de RFC par exemple. Le rôle est un identifiant précédé et suivi des deux points. Par exemple :Les détails sont dans le :RFC:`2822`.Cette notation permettra de créer automatiquement un lien vers le RFC correspondant. Le rôle est facultatif : un rôle par défaut sera appliqué dans ce cas. La valeur par défaut du rôle par défaut (bah oui, y’a deux fois défaut) est
.. role:: surligneEt on peut ensuite l’utiliser où on le souhaite dans notre document :
Essai de :surligne:`texte surligné`.Cette ligne produira le résultat suivant dans la sortie HTML :
Essai de <span class="surligne">texte surligné<span>.Il ne reste qu’à personnaliser les feuilles de style et le tour sera joué.
Les directives
Les directives font partie des blocs au marquage explicite, comme les cibles d’hyperliens, les définitions des substitutions ou les commentaires, par exemple. Les directives sont conçues dès le départ comme un mécanisme d’extension de la syntaxe reST, qui permet de mettre en place de nouveaux éléments sans créer de nouveaux marqueurs syntaxiques (donc sans toucher au parser). Il existe de nombreuses directives standards, décrites dans..image:: foo.png :target: http://libristes-immatures.fr/Une directive est marquée par les deux points consécutifs en début de ligne, suivis d’une espace, du nom de la directive, de deux fois deux points (oui alors non, ça ne fait pas quatre points) et d’une espace. Tout ce qui suit sur la même ligne est considéré comme un argument de la directive (toutes n’en prennent pas forcément). Les lignes suivant la directive forment le reste du bloc de celle-ci : elles doivent être indentées. Les lignes qui débutent par un nom de champ entre : sont les options de la directive, puis suit le contenu de celle-ci. Options et contenus ne sont pas obligatoires, tout dépend de la directive. La directive image a un seul argument, obligatoire, l’URI du fichier source de l’image. Elle peut prendre de nombreuses options, classiques dans le cas des images :
.. admonition:: exemple Ceci est un excellent exemple.Le rendu HTML serait alors :
<div class="admonition-exemple admonition"> <p class="first admonition-title">exemple</p> <p class=”last”>Ceci est un excellent exemple</p> </div>On notera la présence des
Obtenir le résultat souhaité
Personnaliser les feuilles de style
Naturellement, la feuille de style évoque le rendu HTML et sa personnalisation par le biais d’une CSS. Docutils permet bien sûr de le faire, et le HTML produit par défaut utilise l’attribut class de façon systématique. Les classes sont bien conçues et permettent de styler avec beaucoup de précision. En reprenant l’exemple ci-dessus (les directives) :<div class="admonition-exemple admonition"> <p class="first admonition-title">exemple</p> <p class=”last”>Ceci est un excellent exemple</p> </div>On constate la présence de nombreuses classes :
Cette classe est commune à tous les blocs générés par les directives admonition. On peut l’utiliser pour encadrer ceux-ci, par exemple.
Ici aussi, la classe concerne tout le bloc, mais elle est propre aux blocs d’exemple. On pourrait donc les distinguer des autres blocs par une couleur de fond spéciale.
Le premier paragraphe de tous les blocs d’admonition est marqué par cette classe.
Le titre de chaque bloc d’admonition possède cette classe.
Marque le dernier paragraphe de chaque bloc d’admonition.On peut créer une feuille de style from scratch, éventuellement en s’inspirant de la feuille par défaut de la distribution (
/* :Author: :Contact: :Copyright: This stylesheet has been placed in the public domain. Stylesheet for use with Docutils. [Optionally place a more detailed description here.] */ @import url(html4css1.css);La feuille de style peut être incluse dans le HTML final ou simplement liée à celui-ci. Quatre options de

On trouvera quelques détails supplémentaires dans la documentation des feuilles de style [8]
Toutefois, l’utilisation de feuilles de style dans Docutils ne se limite pas au rendu HTML. Il est possible de passer des paramètres dans le rendu LaTeX au travers de ce qui est également appelé une feuille de style. Au sens strict, ce n’en est pas une, mais simplement un fichier dont le contenu sera inclus dans l’en-tête du fichier de sortie LaTeX (avant la ligne \begin{document} donc).
La documentation du rendu LaTeX [9] précise la liste des paramètres qui sont configurables de cette façon. J’utilise ainsi systématiquement :
\setlength{\parindent}{0pt}
\setlength{\parskip}{2ex plus 0.2ex minus 0.2ex}
Ici, les premières lignes des paragraphes ne sont pas indentées, et la valeur de l’espacement vertical entre les paragraphes est un peu supérieure à la valeur par défaut. Deux options de ligne de commande contrôlent l’utilisation de la " feuille de style " LaTex :

Le fichier de configuration
Comme on vient d’en avoir une illustration ci-dessus, la plupart des options de ligne de commande des outils Docutils sont des options POSIX longues. Pour éviter de devoir les préciser à chaque invocation, et ainsi faire honneur à notre animal favori, la loutre, on peut renseigner un (ou des) fichier(s) de configuration avec les options choisies. Ces fichiers de configuration adoptent une syntaxe tout à fait standard et sont composés de sections autour des principales briques de Docutils :$ cat .docutils [html4css1 writer] embed_stylesheet: no stylesheet_path: /home/user/templates/docutils.css
Les mains dans le cambouis
Création de nouveaux rôles pour le texte interprété
On a vu dans la description du texte interprété comment créer un rôle tout simple pour faire duPour plus d’informations, voir le tag :gcu:`python`.Pour obtenir en HTML :
<p>Pour plus d’informations, voir le tag <a href="http://del.icio.us/gcu_squad/python>python</a>.</p>Et un hyperlien équivalent dans un source LaTeX qui sera compilé pour obtenir un document PDF. Le module
1 def rfc_reference_role(role, rawtext, text, lineno, inliner,
2 options={}, content=[]):
3 try:
4 rfcnum = int(text)
5 if rfcnum <= 0:
6 raise ValueError
7 except ValueError:
8 msg = inliner.reporter.error(
9 ‘RFC number must be a number greater than or equal to 1; ‘
10 ‘”%s” is invalid.’ % text, line=lineno)
11 prb = inliner.problematic(rawtext, rawtext, msg)
12 return [prb], [msg]
13 # Base URL mainly used by inliner.rfc_reference, so this is correct:
14 ref = inliner.document.settings.rfc_base_url + inliner.rfc_url % rfcnum
15 set_classes(options)
16 node = nodes.reference(rawtext, ‘RFC ‘ + utils.unescape(text), refuri=ref,
17 **options)
18 return [node], []
Les rôles sont implémentés par la fonction $ cp /usr/bin/rst2html myrst2html.pyAdaptez selon votre distribution, bien sûr. Les applications se trouvent en principe dans
1 #!/usr/bin/python
2
3 „““
4 A minimal front end to the Docutils Publisher, producing HTML.
5 „““
6
7 try:
8 import locale
9 locale.setlocale(locale.LC_ALL, ‚‘)
10 except:
11 pass
12
13 from docutils import nodes, utils
14 from docutils.core import publish_cmdline, default_description
15 from docutils.parsers.rst import roles
16
17 def gcu_reference_role(role, rawtext, text, lineno, inliner,
18 options={}, content=[]):
19 ref = „http://del.icio.us/gcu_squad/%s“ % text
20 roles.set_classes(options)
21 node = nodes.reference(rawtext, utils.unescape(text), refuri=ref,
22 **options)
23 return [node], []
24
25 roles.register_local_role(‚gcu‘, gcu_reference_role)
26
27
28
29 description = (‚Generates (X)HTML documents from standalone reStructuredText ‚
30 ‚sources. ‚ + default_description)
31
32 publish_cmdline(writer_name=‘html‘, description=description)
Les lignes 1 à 11, 14 et 29 à 32 viennent du$ cat truc.txt Essai de texte interprété Voir le lien vers :gcu:`python` pour d’autres informations.Et enfin à le traiter, et à admirer le résultat :
$ myrst2html.py truc.txt truc.html $ tail -n 5 truc.html <p>Essai de texte interprété</p> <p>Voir le lien vers <a class="reference" href="http://del.icio.us/gcu_squad/python">python</a> pour d’autres informations.</p> </div> </body> </html>C’est pas beau ?
Création de nouvelles directives
Important L’API de création de directives a changé après la dernière version " stable " de Docutils, qui est la 0.4 et est celle qui est distribuée sous forme de packages. Ce qui suit concerne la version 0.4 et donc ce qu’on pourrait appeler l’ancienne API, parce qu’il y a de fortes chances que ce soit celle qui est installée sur votre machine. En tout état de cause, la compatibilité avec l’ancienne API est conservée : si vous avez le dernier snapshot, ce qui suit fonctionnera aussi – mais vous pouvez essayer la nouvelle API dans laquelle les directives sont définies par classe, non par fonction. La création de nouvelles directives va souvent de pair avec l’enrichissement de l’arbre créé par Docutils pour représenter le document après le passage par le parser. En effet, si un nouvel élément est créé, il faut pouvoir le représenter dans l’arbre. Il sera ensuite nécessaire d’indiquer au(x)1 #!/usr/bin/env python
2
3 „““
4 A minimal front end to the Docutils Publisher, producing LaTeX.
5 „““
6
7 try:
8 import locale
9 locale.setlocale(locale.LC_ALL, ‚‘)
10 except:
11 pass
12
13 from docutils.parsers.rst.roles import register_canonical_role
14 from docutils import nodes
15 from docutils.writers.latex2e import LaTeXTranslator
16 from docutils.parsers.rst.directives import _directives
17 from docutils.core import publish_cmdline, default_description
18
19
20 # Define LaTeX math node:
21 class latex_math(nodes.Element):
22 tagname = ‘#latex-math’
23 def __init__(self, rawsource, latex):
24 nodes.Element.__init__(self, rawsource)
25 self.latex = latex
26
27 # Register role:
28 def latex_math_role(role, rawtext, text, lineno, inliner,
29 options={}, content=[]):
30 i = rawtext.find(‘`’)
31 latex = rawtext[i+1:-1]
32 node = latex_math(rawtext, latex)
33 return [node], []
34 register_canonical_role(‘latex-math’, latex_math_role)
35
36
37 # Register directive:
38 def latex_math_directive(name, arguments, options, content, lineno,
39 content_offset, block_text, state, state_machine):
40 latex = ‘’.join(content)
41 node = latex_math(block_text, latex)
42 return [node]
43 latex_math_directive.arguments = None
44 latex_math_directive.options = {}
45 latex_math_directive.content = 1
46 _directives[‘latex-math’] = latex_math_directive
47
48
49 # Add visit/depart methods to HTML-Translator:
50 def visit_latex_math(self, node):
51 inline = isinstance(node.parent, nodes.TextElement)
52 if inline:
53 self.body.append(‘$%s$’ % node.latex)
54 else:
55 self.body.extend([‘\\begin{equation*}\\begin{split}’,
56 node.latex,
57 ‘\\end{split}\\end{equation*}’])
58 def depart_latex_math(self, node):
59 pass
60 LaTeXTranslator.visit_latex_math = visit_latex_math
61 LaTeXTranslator.depart_latex_math = depart_latex_math
62
63
64 description = (‘Generates LaTeX documents from standalone reStructuredText ‘
65 ‘sources. ‘ + default_description)
66
67 publish_cmdline(writer_name=’latex’, description=description)
Les ajouts sont facilement repérables :
lignes 14 et 20-25
Définition du nouvel élément (node) pour l’arbre de représentation du document. Celui-ci doit hériter de la classe Utiliser les publishers dans un shell ou un programme
Allez, une petite dernière pour la route : j’aimerais utiliser Docutils pour publier les billets d’un blog, ou un fragment de page HTML. On va donc bricoler une petite application pour ne sortir que le contenu du tag1 #!/usr/bin/python
2
3 „““
4 A minimal front end to the Docutils Publisher, producing the body
5 of an HTML doc.
6 html_parts and html_body borrowed from docutils/examples.py
7 „““
8
9 try:
10 import locale
11 locale.setlocale(locale.LC_ALL, ‚‘)
12 except:
13 pass
14
15 from docutils import core
16
17 def html_parts(input_string, source_path=None, destination_path=None,
18 input_encoding=‘unicode‘, doctitle=1, initial_header_level=1):
19 „““
20 Given an input string, returns a dictionary of HTML document parts.
21
22 Dictionary keys are the names of parts, and values are Unicode strings;
23 encoding is up to the client.
24
25 Parameters:
26
27 - `input_string`: A multi-line text string; required.
28 - `source_path`: Path to the source file or object. Optional,
29 but useful for diagnostic output (system messages).
30 - `destination_path`: Path to the file or object which will
31 receive the output; optional. Used for determining relative
32 paths (stylesheets, source links, etc.).
33 - `input_encoding`: The encoding of `input_string`. If it is an
34 encoded 8-bit string, provide the correct encoding. If it is
35 a Unicode string, use "unicode", the default.
36 - `doctitle`: Disable the promotion of a lone top-level section
37 title to document title (and subsequent section title to document
38 subtitle promotion); enabled by default.
39 - `initial_header_level`: The initial level for header elements
40 (e.g. 1 for "<h1>").
41 """
42 overrides = {‘input_encoding’: input_encoding,
43 ‘doctitle_xform’: doctitle,
44 ‘initial_header_level’: initial_header_level}
45 parts = core.publish_parts(
46 source=input_string, source_path=source_path,
47 destination_path=destination_path,
48 writer_name=’html’, settings_overrides=overrides)
49 return parts
50
51 def html_body(input_string, source_path=None, destination_path=None,
52 input_encoding=’unicode’, output_encoding=’unicode’,
53 doctitle=1, initial_header_level=1):
54 """
55 Given an input string, returns an HTML fragment as a string.
56
57 The return value is the contents of the <body> element.
58
59 Parameters (see `html_parts()` for the remainder):
60
61 - `output_encoding`: The desired encoding of the output. If a Unicode
62 string is desired, use the default value of "unicode" .
63 """
64 parts = html_parts(
65 input_string=input_string, source_path=source_path,
66 destination_path=destination_path,
67 input_encoding=input_encoding, doctitle=doctitle,
68 initial_header_level=initial_header_level)
69 fragment = parts[‘html_body’]
70 if output_encoding != ‘unicode’:
71 fragment = fragment.encode(output_encoding)
72 return fragment
73
74
75 import sys
76
77 thebody = html_body(sys.stdin.read(),input_encoding=’utf8’)
78
79 sys.stdout.write(thebody)
Le code pourrait presque se passer de commentaires. On peut faire beaucoup plus court, mais on a préféré utiliser les deux fonctions En guise de conclusion
Comme vous l’avez probablement constaté, Docutils promet des heures d’amusement pour peu que l’on soit un peu curieux. N’oublions pas toutefois que le but initial de cet outil est de permettre de produire rapidement une documentation de qualité, en gérant la source de celle-ci sous forme de fichiers textes, avec tous les avantages que cela présente. Dans cet esprit, on peut trouver autour de Docutils un certain nombre d’outils intéressants. On n’oubliera pas non plus qu’il existe des alternatives à cette solution.Les goudizes
Évidemment, il ne s’agit pas d’être exhaustif. Le premier outil est particulièrement utile pour tester Docutils sans l’installer. Le siteLa concurrence
Pour finir, je parlerai brièvement de deux projets " concurrents " de Docutils, qui sont également écrits en Python. Il y en a bien sûr d’autres... Le projet asciidoc [22] de Stuart Rackham semble assez proche, au moins dans ses objectifs. Il est actif et utilisé (la documentation de git [23] par exemple est réalisée avec), et la feuille de style de base est, il faut l’avouer, assez élégante. De son côté, txt2tags [24] se démarque complètement de la philosophie de Docutils, et le revendique, quoique sans jamais nommer son concurrent. Il s’agit en effet d’un seul programme Python qui accomplit l’ensemble du travail. C’est un outil abouti, qui offre de nombreux formats en sortie, dont par exemple celui du wiki MoinMoin [25] ou du système de présentation Magic Point [26]. J’ai également utilisé ces deux outils pour différents projets, et je pense que la préférence pour l’un ou l’autre est affaire de goût avant tout. Il reste que Docutils présente une documentation très développée, en particulier vers les utilisateurs avancés ou bricoleurs, ce qui ne manquera pas de chatouiller le hacker qui sommeille en vous. Amusez-vous bien !Liens
- [1] http://docutils.sourceforge.net/README.txt#installation
- [2] http://docutils.sourceforge.net/README.txt
- [3] http://docutils.sourceforge.net/mirror/setext.html
- [4] http://dev.zope.org/Members/jim/StructuredTextWiki/FrontPage/
- [5] http://structuredtext.sourceforge.net/HISTORY.html
- [6] http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html
- [7] http://docutils.sourceforge.net/docs/ref/rst/directives.html
- [8] http://docutils.sourceforge.net/docs/howto/html-stylesheets.html
- [9] http://docutils.sourceforge.net/docs/user/latex.html
- [10] http://docutils.sourceforge.net/docs/user/config.html
- [11] http://docutils.sourceforge.net/docs/howto/rst-roles.html
- [12] http://www.gcu.info/
- [13] http://docutils.sourceforge.net/FAQ.html
- [14] http://docutils.sourceforge.net/sandbox/jensj/latex_math/tools/rst2latex.py
- [15] http://docutils.sourceforge.net/docs/howto/rst-directives.html
- [16] http://docutils.sourceforge.net/docs/api/publisher.html
- [17] http://rst2a.com/
- [18] http://www.rexx.com/~dkuhlman/odtwriter.html
- [19] http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office
- [20] http://www.voidspace.org.uk/python/rest2web/
- [21] http://skawina.eu.org/mikolaj/vst.html
- [22] http://www.methods.co.nz/asciidoc/
- [23] http://git.or.cz/
- [24] http://txt2tags.sourceforge.net/
- [25] http://moinmo.in/
- [26] http://member.wide.ad.jp/wg/mgp/
Retrouvez cet article dans : Linux Magazine 104





Laissez une réponse
Vous devez avoir ouvert une session pour écrire un commentaire.