#!/usr/bin/perl -w
use warnings;
use strict;
# ce qui suit est *essentiel* au bon fonctionnement
# des expressions régulières utilisées ici.
# Tout doit etre en utf8.
use locale;
use utf8;
use encoding "utf8";
#binmode( STDIN, ':utf8' );
#binmode( STDOUT, ':utf8' );
#use feature 'unicode_strings';
# variable pour savoir si on génère un sommaire.
my $sommaire = 0;
# variable pour savoir si on génère aussi les numéros de section
my $sections = 0;
# variable pour savoir si on fait du ménage (clean) sur les
# titres de section (pas de formatage : gras, italique) ni de
# ":" en fin de titre.
my $clean_sections = 0;
my $usage = <<"EOF";
usage: $0 [-h|-t|-c|-n] [<fichier généré par antiword>]*
Résumé:
Ce script génère du texte conforme aux usages de formatage de dokuwiki à partir d'un
fichier texte résultat d'une transformation, par antiword, d'un fichier word bien
formaté.
Par défaut, le texte à transformer se tappe à l'entrée standard.
Options:
-h Cette aide
-t Génère la table des matières extraite du fichier donné en attribut
-n Génère aussi les numéros de sections dans les sections
-c Génère des noms de section cleans (pas de formatage du texte des sections)
fichier_antiword Le fichier à utiliser pour produire un texte conforme à dokuwiki
Présentation:
script de génération de code dokuwiki à partir d'un
document texte généré par "antiword -f -m 8859-1 -w 0 nom_fichier_word.doc | iconv -f iso-8859-1 -t utf-8".
Les options d'antiword sont importantes : -w 0 signifie
que les paragraphes ne sont pas justifiés mais tiennent sur une
seule ligne. C'est important pour ce qui concerne les listes
à puces. Si tel n'était pas le cas, on trouverait des passages
à la ligne dans les listes entrainant un formatage incorrect
des sous-listes par exemple.
Format accepté:
== Règles de transformations et présuppositions
* il y a un sommaire reprenant chaque ligne. Ce sommaire commence
par la chaine "Contenu du document :" avec un espace final qui
peut etre insécable ou non et d'éventuels espaces après les ":".
* Ce sommaire servira à construire la table des matières soit un
tableau des différents titres de section et sous-section. Une fois ce
tableau établi, les titres sont recherchés dans la page les uns après
les autres. Ils sont remplacés par un formatage wiki correct en
fonction de la profondeur de section (on se base sur le format de
numérotation à savoir : <chiffres>[.<chiffres>]*. Le nombre de "."
indique donc la profondeur de section.
* Enfin, comme dans tout sommaire, en correspondance du titre et de
la numérotation de la section, il doit y avoir un numéro de page.
Ce numéro est obligatoire pour permettre à l'algorithme utilisé
de fonctionner.
En dokuwiki, nous avons cela :
* h1 ====== ====== Correspond au titre principal (non utilisé sauf
pour le titre mais celui-ci n'est pas reconnu par la procédure).
* h2 ===== ===== Les titres de sections sont ici
* h3 ==== ==== Titres des sous-sections (celles qui ont un "." dans
leur numérotation).
* etc.
Les titres de section doivent etre encadrés par une ligne vide (ne
contenant éventuellement que des espaces) de part et d'autre du
titre lui-meme.
=== Exemple de format d'entrée accepté
>>> Début document exemple <<<
Plein de choses avant. Traité seulement pour les apparitions de
- début liste à puce
/toto/ en italique
*gras* en gras
_souligné_
Contenu du document :
1 Introduction 1
2 Présentation 2
2.1 Généralités 4
2.2 Détails 8
2.2.1 Aspects particuliers 15
3 Conclusion 20
1 Introduction
ipsum temporis megna et terra optemporum _isque_
2 ...
etc
>>> Fin document exemple <<<
=== Sortie de l'exemple court précédent
>>> Début document généré <<<
Plein de choses avant. Traité seulement pour les apparitions de
* début liste à puce
//toto// en italique
**gras** en gras
_souligné_
===== Introduction =====
ipsum temporis megna et terra optemporum __isque__
===== ... =====
etc
>>> Fin document généré <<<
=== Limitations :
Le titre principal (h1 correspondant à ====== ======) n'est pas
modifié pour etre formaté correctement au format wiki.
EOF
# traitement de la ligne de commande.
if (scalar(@ARGV) >= 1) {
if ($ARGV[0] eq "-t") {
$sommaire = 1;
} elsif ($ARGV[0] eq "-h") {
print $usage;
exit 0;
} elsif ($ARGV[0] eq "-n") {
$sections = 1;
} elsif ($ARGV[0] eq "-c") {
$clean_sections = 1;
}
shift(@ARGV);
}
# fonction syntaxe_dokuwiki:
#
# arguments attendus :
# - un seul : une chaine de caractères.
#
# fonction :
# - rend une chaine de caractère qui est conforme au
# formatage de dokuwiki
#
sub syntaxe_dokuwiki {
my $ligne = shift;
# on trim à gauche seulement - avant le reste
$ligne =~ s/^\s+//g;
# remplacement des /italiques/ par //italiques//
$ligne =~ s|/([^/]+)/|//$1//|g if m|/[^/]+/|;
# remplacement des /en gras/ par //en gras//
$ligne =~ s|\*([^*]+)\*|**$1**|g if m|\*[^\*]+\*|;
# remplacement des _soulignés_ par __soulignés__
$ligne =~ s|_([^_]+)_|__$1__|g;
# remplacement des "-" en début de ligne par " *"
$ligne =~ s|^\s*- | * |g;
# remplacement des "o" en début de ligne par " *"
$ligne =~ s|^\s*o | * |g;
# remplacement des "▪" (utf8 : \xe2\x96\xaa) en début de ligne par " *"
# my $carre = "\xe2\x96\xaa";
# avec la nouvelle méthode utilisant antiword latin1 + iconv , ce ne sont plus
# des carrés mais des "." simples.
my $carre = quotemeta(".");
$ligne =~ s|^\s*$carre | * |g;
return $ligne;
}
# fonction creer_section_wiki:
#
# variables:
# - titre: le titre qu'il faut mettre avec le bon formatage
# - niveau : le niveau de profondeur du titre
#
sub creer_section_wiki {
my $titre = shift;
my $niveau = shift;
my $nsection = shift;
my $section = "";
for (my $k = 1 ; $k <= 6 - $niveau; $k++) {
$section .= "=";
}
return "$section " . ($nsection ? "$nsection - " : "") . $titre . " $section\n";
}
# == Variables
# le tableau des indexes du sommaire
my @index;
# $encadre_titre : variable permettant de savoir si un titre est entouré,
# de part et d'autre, par deux lignes ne comportant éventuellement que des
# espaces ou simplement vide.
# valeurs :
# * 0 : aucune ligne vide détectée
# * 1 : une ligne vide détectée
# * 2 : ligne vide + titre
# * 3 : ligne vide + titre + ligne vide
my $encadre_titre = 0;
# $titre_trouve : valeur booléenne qui indique si un titre du sommaire
# est trouvé sur la ligne courante traitée.
my $titre_trouve = 0;
# $sommaire_traite : valeur booléenne qui indique si le sommaire a été
# traité et si le tableau des entrées de ce sommaire est constitué.
my $sommaire_traite = 0;
# $debut_sommaire : valeur booléenne qui indique si on entre dans le
# début du sommaire - à savoir : si "Contenu du document :" est passé
# *et* si on est dans les lignes vides séparant ce texte du contenu
# véritable du sommaire.
my $debut_sommaire = 0;
# $i : index du tableau des titres des sections et sous-sections.
my $i = 0;
# $j : index courant du titre à chercher dans le tableau @index.
my $j = 0;
while (<>) {
chomp;
# print "[debug] - ligne = '$_'\n";
# == génération du sommaire.
#
# attention, il y a un espace insécable avant les ":" - couvert par \h et pas \s.
if (m!^(?:\s| |[*/_])*Contenu du document(?:\s| ):(?:\s| |[*/_])*$!) {
# print "[debug] - entrée dans contenu du document\n";
# on entre dans le début du sommaire
$debut_sommaire = 1;
# On avance dans la lecture du fichier
while (<>) {
# print "[debug] - ligne = '$_'\n";
# si on est à peine rentré dans le sommaire,
# - on n'en sort pas avec une succession de lignes vides
# - on peut en sortir après avoir eu au moins une ligne
# contenant quelque chose.
if (/^\s*$/ && $debut_sommaire == 1) {
next;
} elsif (! /^\s*$/ && $debut_sommaire == 1) {
$debut_sommaire = 0;
} else {
$debut_sommaire = 0;
}
# si ligne vide, on quitte car toutes les sections du sommaire ont été traitées
last if (/^\s*$/);
# Cas général :
# - dans ce cas, toutes les lignes suivantes qui ne sont pas
# des lignes vides sont des éléments de titre
# Le format de ces lignes est du type :
# - espaces suivi de numéros de sections et sous-sections séparées par des "."
# et finissant par des espaces avec un nombre terminant la ligne.
if (/^\s*(\d+(\.\d+){0,})\s+(.*)\d+$/) {
my $num_section = $1;
# traitement de la section (combien de sous sections)
# le niveau de sous-section est le nombre de "." dans le numéro de section.
my $niveau = scalar(split(/\./, "$num_section"));
# on trim le titre (suppression des espaces avant et après)
my $titre = $3;
$titre =~ s/^\s+|\s+$//g;
# impression du tableau dans le cas où le sommaire est demandé (-t)
print "$niveau|$titre|$num_section\n" if $sommaire;
# insertion du titre et du niveau dans un tableau
# contenant tout le sommaire.
$index[$i]{"niveau"} = $niveau;
$index[$i]{"titre"} = $titre;
$index[$i]{"numero"} = $num_section;
$i++;
}
# fin du traitement du sommaire
$sommaire_traite = 1;
}
} else {
# print "[debug] - encadre_titre courant = $encadre_titre\n";
# == traitement de la génération du document
# traitement des sections une par une (index de sous-section courante = $j)
if ($sommaire_traite) {
my $titre_a_chercher = $index[$j]{"titre"};
#print "[debug] - nouveau titre à chercher = '$titre_a_chercher'\n";
# ligne vide et précédée par une ligne vide ou une pleine
if (/^\s*$/ && ($encadre_titre == 0 || $encadre_titre == 1)) {
$encadre_titre = 1;
print "$_\n" if !$sommaire;
next;
# ligne non vide et précédée par une ligne vide
} elsif (!/^\s*$/ && $encadre_titre == 1) {
$encadre_titre = 2;
# print "[debug] - titre à chercher : '$titre_a_chercher'\n";
# print "[debug] - chaine = '$_'\n";
# Le titre à chercher ne doit pas etre vide et il doit voir les
# caractères spéciaux qu'il contient échappés pour ne pas avoir
# de problèmes dans la recherche du modèle.
# Remarque : utiliser une recherche de chaîne de caractères précise
# (substring) plutôt que d'utiliser les expressions régulières
# serait plus sûr et efficace mais peut-être aussi plus juste
# sémantiquement.
my $titre_quote_meta = quotemeta($titre_a_chercher) if $titre_a_chercher;
# remarque importante sur le titre :
# dans le sommaire, il n'y a pas de formatage (gras/italique/souligné)
# alors qu'il peut en exister dans le titre réel, dans le corps
# du document. Pour chaque ligne potentiellement «titre», il faut
# commencer par supprimer le formatage.
# $ligne_traitee : la ligne moins les caractères */_
my $ligne_traitee = $_;
$ligne_traitee =~ s|[_/*]||g;
# expression régulière compliquée pour les titres
# - espaces possibles suivis de chiffres (possible) suivis d'espaces
# possibles ou des caractères de mise en forme /, * ou _ (possible)
# suivi par le titre suivi par des espaces ou /, *, _ (possible).
if ($titre_a_chercher && ($ligne_traitee =~ m!^(?:\s| )*([/*_]*)\d*(?:\s| )*([*/_]*)($titre_quote_meta)((?:\s| )*)([*/_]*)(?:\s| )*$!)) {
# print "[debug] - titre_quote_meta = '$titre_quote_meta'\n";
# print "[debug] - ligne brute = '$_'\n";
my $ligne = "$1$2$3$4$5";
# print "[debug] - ligne = '$ligne'\n";
$titre_trouve = 1;
# print "[debug] - titre trouvé\n";
my $ligne_suivante = <>;
chomp($ligne_suivante);
if (!$ligne_suivante =~ /^\s*$/) {
print "[debug] - encadre_titre = $encadre_titre ; titre_a_chercher = $titre_a_chercher\n";
print "[debug] - ERROR - indication erreur : la ligne suivant le titre n'est pas une ligne vide ! Bizare !\nligne ='$_'\n";
$encadre_titre = 0;
} else {
# print "[debug] - La ligne suivante est bien une ligne vide.\n";
# print "[debug] - La ligne qui fait office de titre est : '$ligne'.\n";
# computation du titre au format wiki
# On passe plutôt la ligne que le titre car les titres
# peuvent avoir du formatage particulier en
# plus de celui qui apparaît dans le sommaire.
if ($clean_sections) {
$titre_a_chercher =~ s/^(.*?)(?: |\s)*:(?: |\s)*$/$1/;
$_ = &creer_section_wiki($titre_a_chercher, $index[$j]{"niveau"}, $sections ? $index[$j]{"numero"} : "");
} else {
$_ = &creer_section_wiki($ligne, $index[$j]{"niveau"}, $sections ? $index[$j]{"numero"} : "");
}
# on a eu une ligne suivante vide, donc il faut le dire
$encadre_titre = 1;
}
$j++; # pour passer au prochain titre de section à chercher.
} else {
$titre_trouve = 0;
$encadre_titre = 0;
}
} elsif (/^\s*$/ && $encadre_titre == 2) {
$encadre_titre = 3;
# print "[debug] - bon modèle de titre : ligne vide + ligne pleine + ligne vide = $_\n" if !$sommaire;
print "$_\n" if !$sommaire;
next;
} else {
$encadre_titre = 0;
}
}
my $ligne = &syntaxe_dokuwiki($_);
# si on ne veut pas afficher que le sommaire :
print "$ligne\n" if !$sommaire;
}
}
|