gmi2tex.awk : gemtext vers LaTeX

2022-01-21T15:52:35Z

Puisque je n'écris plus qu'exclusivement en gemtext, j'ai écrit un outil de conversion du gemtext vers du LaTeX en awk. J'avais auparavant réalisé la même chose en C, mais puisque j'aime beaucoup le awk, je n'ai pas résisté. Les sources sont là :

gmi2tex.awk

J'en ai profité pour mettre à jour l'outil qui fait de même vers du html. Il y avait un oubli de listes qui n'étaient pas fermés si elles étaient en fin de page et donc pas suivies d'une ligne vide. Par la même occasion, j'ai ajouté une fonction "trim" qui bien que pas nécessaire évite de laisser des espaces inutiles dans le code source des pages.

gmi2html.awk

Pour en revenir à l'outil de conversion de gemtext vers LaTeX, il permet de sortir du code LaTeX valide à inclure dans un autre document qui lui appelle tous les "usepackage" qui vont bien. Oui je sais, il y a plus simple que LaTeX, mais je ne suis pas prêt à apprendre troff.

Traîter du gemtext avec awk est tout à fait adapté puisque par définition, du gemtext doit être lu ligne par ligne.

Voici à quoi ressemble le code, avec quelques commentaires pour expliquer un peu la méthode.

On commence avec une fonction pour traiter les symboles spéciaux de LaTex dans une chaîne dze caractères. Bon, là c'est pénible à lire, il faut l'admettre. Chaque "\" doit être échappé pour être considéré comme tel. De même, "^" et "$" définissent les débuts et fins de ligne pour une regex, il faut les échapper. Enfin, "&" permet de recopier le texte matché, alors il faut l'échapper lui aussi.

function escape(s) {
    gsub("\\\\","\\textbackslash",s) # must be first
    gsub("~","\\textasciitilde",s)
    gsub("\\^","\\textasciicircum",s)
    gsub("&","\\\\&",s)
    gsub("%","\\%",s)
    gsub("\\$","\\$",s)
    gsub("#","\\#",s)
    gsub("_","\\_",s)
    gsub("{","\\{",s)
    gsub("}","\\}",s)
    return s
}

Ensuite vient une fonction trim qui retire les espaces inutiles en début et fin de ligne.

function trim(s) {
	sub(/^[[:blank:]]+/, "", s);
	sub(/[[:blank:]]+?$/, "", s);
	return s
}

On commence à effectivement traîter le gemtext.

Ici, on active ou désactive le mode verbatim si on a rencontré la chaîne "```" en début de ligne. Ce mode permet d'afficher du code tel quel.

/^```/ {
	if (verbatim) { print "\\end{verbatim}"} else { print "\\begin{verbatim}" }
	verbatim = (verbatim + 1) % 2 # toggle verbatim
	next
}

On veilel ensuite à conserver les sauts de ligne si on a laissé une ligne vide, ou bien on ferme une liste si on était dedans :

/^$/ {
	if (ul == 1) { ul = 0; print "\\end{itemize}" }
	else { print "\\bigskip" }
	next
}

En parlant de liste, on en ouvre une si on en détecte une ligne, sinon on ajoute un item.

!verbatim && /^\*/ {
	if (ul == 0) { ul = 1; print "\\begin{itemize}" }
	$1=""
	printf "\\item %s\n", trim(escape($0))
	next
}

Voici la gestion des liens. Ici je voulais pouvoir aussi traîter les images. Il faut donc récupérer le lien, puis éventuellement le titre. Si c'est une image, on l'encapsule dans un élément figure

!pre && /^=>/ {
	href = escape($2)
	if ( NF > 2 ) { $1 = ""; $2 = ""; title = escape($0) }
	else { title = href }
	# trim
	sub(/^[[:blank:]]+/, "", title);
	sub(/[[:blank:]]+$/, "", title);
	sub(/^[[:blank:]]+/, "", href);
	sub(/[[:blank:]]+$/, "", href);
	if ( match(tolower(href), "\.jpg$")  || \
	     match(tolower(href), "\.jpeg$") || \
	     match(tolower(href), "\.png$")  || \
	     match(tolower(href), "\.gif$")  || \
	     match(tolower(href), "\.eps$")  || \
	     match(tolower(href), "\.pdf$") ) {  \
		printf "\\begin{figure}[h]\n"
		if (length(title) > 0) { printf "\\caption{%s}\n", title }
		printf "\\includegraphics[width=\\textwidth]{%s}\n", href
		printf "\\end{figure}\n"
	} else {
		printf "\\href{%s}", href
		if (length(title) > 0) { printf "{%s}", title }
		printf "\n"
	}
	next
}

Voici les diverses sections, c'est tout bête : il suffit de virer le premier champ de la ligne (les "#") puis mettre le reste dans des "\section" :

!pre && /^###/ { $1=""; printf "\\subsubsection{%s}\n", trim(escape($0)); next }
!pre && /^##/  { $1=""; printf "\\subsection{%s}\n", trim(escape($0)); next }
!pre && /^#/   { $1=""; printf "\\section{%s}\n", trim(escape($0)); next }
!pre && /^>/   { $1=""; printf "\\begin{quote}\n%s\n\\end{quote}\n", trim(escape($0)); next }

Enfin, si on est dans un mode verbatim, on affiche la ligne sans la modifier :

verbatim { print $0; next}

Sinon, on échappe le contenu pour tous les autres cas de figure:

1 { printf "\n%s\n", escape($0) }

Pour finir, on n'oublie pas de fermer les sections itemize qui ne le seraient pas :

END {
	if (ul == 1) { ul = 0; print "\\end{itemize}\n" }
}

Une réaction?

Envoyez votre commentaire par mail.

Mode d'emploi de la liste de diffusion pour recevoir les réponses.