Retour Suite
sylvainmahe.site LE SITE de Sylvain Mahé contact@sylvainmahe.site
Article : Sylvain Mahé contact@sylvainmahe.site Création automatique d'un livre à partir d'une arborescence de fichiers Attention ce projet est en cours, c'est pourquoi cet article est incomplet ! Cette routine (pour le système d'exploitation Linux que j'utilise) permet de générer automatiquement un livre complet sous la forme d'un seul fichier, via une arborescence de fichiers (organisation hiérarchique des fichiers dans des répertoires) dont le contenu ASCII est extrait : C'est le contenu ASCII (pour "American Standard Code for Information Interchange" ou code américain normalisé pour l'échange d'information) de vos fichiers qui est imprimé dans le livre, contenu prenant ainsi la forme de caractères normalisés qui peut être issu de divers langages, soit par exemple du binaire, de l'hexadécimal, du C++, ou tout autre caractère imprimable. Photos bientôt disponibles. Ainsi ce que je propose ici est un outil qui, bien utilisé, vous permettra de sauvegarder vos fichiers sur des supports imprimés (livres, documents divers), via la simple génération d'un seul fichier à texte continu .txt (texte) qu'il convient par la suite de convertir en .pdf (pour "portable document format" ou format de document portable, c'est le format de fichier pris en charge par tous les imprimeurs) à l'aide du programme gedit (le programme gedit conserve à la perfection la pagination du livre). gedit est généralement installé par défaut sur les distributions Linux, mais si vous ne possédez par ce programme (ou paquet), voici la ligne de commande (à écrire dans le terminal) pour l'installer : sudo apt-get install gedit Ma routine d'automatisation prend donc en entrée une arborescence de fichiers, elle permet de choisir les dimensions des pages du livre en nombre de caractères pour la largeur, et en nombre de lignes pour la hauteur (cohérent avec votre conversion en PDF), ainsi qu'une marge de reliure spécifiée en nombre de caractères de large qui servira à positionner le texte en retrait par rapport à la reliure du livre, ceci afin de faciliter l'ergonomie de lecture. Si besoin, il est possible également de trier manuellement l'ordre d'apparition des fichiers dans le livre, ce qui peut être intéressant si le tri alphanumérique par défaut n'est pas souhaité. Après quoi un fichier nommé book.txt est créé. Ce fichier répertorie tout le contenu des fichiers de l'arborescence, avec la pagination, la numérotation des lignes, la numérotation des pages (paires et impaires), et la table des fichiers en toute fin du livre. Cette routine (.sh) contient le programme suivant : #!/bin/bash bashString="__BASH_" nTable=0 next=false countLineTotal=0 rm book.txt -f echo "Page width (maximum line length) ?" read widthPage echo -e "\nPage height (maximum line number per page) ?" read heightPage echo -e "\nGutter margin (indented text close to the gutter) ?" read marginGutter clear shopt -s globstar for dir in */ do for file in $dir**/* do if [ -f $file ] then fileTable[countTable]=$file ((countTable++)) fi done done while [ $next == false ] do echo "Sort option ?" echo "a = Alphanumeric sort" echo "m = Manual sort" read menuOptionSort if [ $menuOptionSort == "a" ] || [ $menuOptionSort == "m" ] then next=true fi clear done if [ $menuOptionSort == "m" ] then next=false while [ $next == false ] do echo "Manual sort :" echo "e = Exit" echo "s = Save file tree (file.tree)" echo "l = Load file tree (file.tree)" echo "d = Delete file tree (file.tree)" echo "" echo "File tree :" for ((n=0; n < countTable; n++)) do if [ $n == $nTable ] then echo $(($n+1))" = "${fileTable[n]}" <" else echo $(($n+1))" = "${fileTable[n]} fi done read menuManualSort if [ $menuManualSort == "e" ] then next=true elif [ $menuManualSort == "s" ] then rm file.tree -f for ((n=0; n < countTable; n++)) do echo ${fileTable[n]} >> file.tree done elif [ $menuManualSort == "l" ] then nLine=0 while read line do fileTable[$nLine]=$line ((nLine++)) done < file.tree elif [ $menuManualSort == "d" ] then rm file.tree -f elif (($menuManualSort >= 1 && $menuManualSort <= $countTable)) then saveTable=${fileTable[$nTable]} fileTable[$nTable]=${fileTable[$((menuManualSort-1))]} fileTable[$((menuManualSort-1))]=$saveTable fi clear if (($nTable == $countTable - 1)) then nTable=0 else ((nTable++)) fi done fi widthLine=$((widthPage-marginGutter)) for ((nTable=0; nTable < countTable; nTable++)) do echo ${fileTable[nTable]} sizeRaw=$(stat -c "%s" ${fileTable[nTable]}) if [ $sizeRaw == 0 ] then size="(0 octet)" elif [ $sizeRaw == 1 ] then size="(1 octet)" elif (($sizeRaw < 1024)) then size="("$sizeRaw" octets)" elif (($sizeRaw < 1048576)) then sizeInteger=$((sizeRaw/1024)) sizeDecimal=$(echo "scale=1;("$sizeRaw/"1024)"-$((sizeRaw/1024)) | bc -l) sizeNumberOne=$(echo "scale=1;"$sizeRaw/"1024" | bc -l) sizeNumberFull=$(echo $sizeRaw/"1024" | bc -l) if [ $sizeDecimal == 0 ] then if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size="("$sizeRaw" octets = "$sizeInteger"Kio)" else size="("$sizeRaw" octets ≈ "$sizeInteger"Kio)" fi else if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size="("$sizeRaw" octets = "$sizeNumberOne"Kio)" else size="("$sizeRaw" octets ≈ "$sizeNumberOne"Kio)" fi fi elif (($sizeRaw < 1073741824)) then sizeInteger=$((sizeRaw/1048576)) sizeDecimal=$(echo "scale=1;("$sizeRaw/"1048576)"-$((sizeRaw/1048576)) | bc -l) sizeNumberOne=$(echo "scale=1;"$sizeRaw/"1048576" | bc -l) sizeNumberFull=$(echo $sizeRaw/"1048576" | bc -l) if [ $sizeDecimal == 0 ] then if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size=$sizeRaw" octets ("$sizeInteger"Mio)" else size=$sizeRaw" octets (≈ "$sizeInteger"Mio)" fi else if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size=$sizeRaw" octets ("$sizeNumberOne"Mio)" else size=$sizeRaw" octets (≈ "$sizeNumberOne"Mio)" fi fi fi echo "Fichier "${fileTable[nTable]}" "$size" :" | fold -s -w$widthLine >> tmp3 echo "" >> tmp3 cp ${fileTable[nTable]} tmp2 sed -i tmp2 -e "s/ /"$bashString"SPACE/g" -e "s/\t/"$bashString"TABULATION/g" -e "s/*/"$bashString"STAR/g" -e "s/\\\/"$bashString"ESCAPE/g" countTmpLine=$(wc -l < tmp2) nTmpLine=1 while read line do if (($countTmpLine < 10)) then widthPrefix=4 spacePrefix=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" fillSpace="" elif (($countTmpLine < 100)) then widthPrefix=5 spacePrefix=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" if (($nTmpLine < 10)) then fillSpace=$bashString"SPACE" else fillSpace="" fi elif (($countTmpLine < 1000)) then widthPrefix=6 spacePrefix=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" if (($nTmpLine < 10)) then fillSpace=$bashString"SPACE"$bashString"SPACE" elif (($nTmpLine < 100)) then fillSpace=$bashString"SPACE" else fillSpace="" fi elif (($countTmpLine < 10000)) then widthPrefix=7 spacePrefix=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" if (($nTmpLine < 10)) then fillSpace=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" elif (($nTmpLine < 100)) then fillSpace=$bashString"SPACE"$bashString"SPACE" elif (($nTmpLine < 1000)) then fillSpace=$bashString"SPACE" else fillSpace="" fi elif (($countTmpLine < 100000)) then widthPrefix=8 spacePrefix=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" if (($nTmpLine < 10)) then fillSpace=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" elif (($nTmpLine < 100)) then fillSpace=$bashString"SPACE"$bashString"SPACE"$bashString"SPACE" elif (($nTmpLine < 1000)) then fillSpace=$bashString"SPACE"$bashString"SPACE" elif (($nTmpLine < 10000)) then fillSpace=$bashString"SPACE" else fillSpace="" fi fi echo $line | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"TABULATION/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"ESCAPE/\\\/g" | fold -s -w$((widthLine-widthPrefix)) | sed -e "s/ /"$bashString"SPACE/g" -e "s/*/"$bashString"STAR/g" -e "s/\\\/"$bashString"ESCAPE/g" > tmp1 nTmpSubline=1 while read subline do if [ $nTmpSubline == 1 ] then echo $fillSpace$nTmpLine" : "$subline | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"ESCAPE/\\\/g" >> tmp3 else echo $spacePrefix$subline | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"ESCAPE/\\\/g" >> tmp3 fi ((nTmpSubline++)) done < tmp1 ((nTmpLine++)) done < tmp2 rm tmp1 -f rm tmp2 countLine=$(wc -l < tmp3) nLine=0 fillPage=$((heightPage-2)) for ((n=0; n < countLine - 1; n++)) do if (($nLine == $heightPage - 3)) then fillPage=$((fillPage+(heightPage-2))) nLine=0 else ((nLine++)) fi done for ((n=0; n < fillPage - countLine; n++)) do echo "" >> tmp3 done titlePageTable[nTable]=${fileTable[nTable]} if [ $countLineTotal == 0 ] then numberPageTable[nTable]=1 else numberPageTable[nTable]=$(((countLineTotal/(heightPage-2))+1)) fi countLineTotal=$(wc -l < tmp3) done nLine=0 numberPageContent=1 flipFlop=false echo "" for ((n=0; n < countLineTotal; n++)) do if (($nLine == $heightPage - 1)) then echo "Pagination "$((n+1)) sed -i tmp3 -e "$n i \\\\" if [ $flipFlop == false ] then lenghtNumberPageContent=${#numberPageContent} fillSpace="\ " for ((nCharacter=1; nCharacter < widthLine - lenghtNumberPageContent; nCharacter++)) do fillSpace=$fillSpace"\ " done sed -i tmp3 -e "$((n+1)) i $fillSpace$numberPageContent" flipFlop=true else sed -i tmp3 -e $((n+1))"i"$numberPageContent flipFlop=false fi ((numberPageContent++)) elif [ $nLine == $heightPage ] then nLine=0 fi countLineTotal=$(wc -l < tmp3) ((nLine++)) done if [ $flipFlop == false ] then lenghtNumberPageContent=${#numberPageContent} fillSpace=$bashString"SPACE" for ((n=1; n < widthLine - lenghtNumberPageContent; n++)) do fillSpace=$fillSpace$bashString"SPACE" done echo -e "\n"$fillSpace$numberPageContent | sed -e "s/"$bashString"SPACE/ /g" >> tmp3 else echo -e "\n"$numberPageContent >> tmp3 fi echo "" echo -e "Table des fichiers :\n" >> tmp3 lenghtMaxPageNumber=${#numberPageTable[$((countTable-1))]} lenghtMaxPageTitle=$(((widthLine-3)-${#numberPageTable[$((countTable-1))]})) for ((nTable=0; nTable < countTable; nTable++)) do echo "File table "${numberPageTable[nTable]} echo ${titlePageTable[$nTable]} | fold -s -w$lenghtMaxPageTitle > tmp4 countTmpLine=$(wc -l < tmp4) nTmpLine=1 point="." while read line do if [ $nTmpLine != $countTmpLine ] then echo $line >> tmp3 else lenghtLastLine=$((${#line}+${#numberPageTable[nTable]})) for ((nCharacter=0; nCharacter < widthLine - (lenghtLastLine + 3); nCharacter++)) do point=$point"." done echo $line" "$point" "${numberPageTable[nTable]} >> tmp3 fi ((nTmpLine++)) done < tmp4 done rm tmp4 countLineTotal=$(wc -l < tmp3) nLine=0 fillPage=$heightPage for ((n=0; n < countLineTotal - 1; n++)) do if (($nLine == $heightPage - 1)) then fillPage=$((fillPage+heightPage)) nLine=0 else ((nLine++)) fi done for ((n=0; n < fillPage - countLineTotal; n++)) do echo "" >> tmp3 done if [ $marginGutter != 0 ] then sed -i tmp3 -e "s/ /"$bashString"SPACE/g" -e "s/*/"$bashString"STAR/g" -e "s/\\\/"$bashString"ESCAPE/g" fillSpace=$bashString"SPACE" for ((n=1; n < marginGutter; n++)) do fillSpace=$fillSpace$bashString"SPACE" done nLine=0 nLineDisplayed=1 flipFlop=false echo "" while read line do if [ $nLine == $heightPage ] then echo "Pagination "$nLineDisplayed if [ $flipFlop == false ] then flipFlop=true else flipFlop=false fi nLine=0 fi if [ $flipFlop == false ] then echo $fillSpace$line | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"ESCAPE/\\\/g" >> tmp5 else echo $line | sed -e "s/"$bashString"SPACE/ /g" -e "s/"$bashString"STAR/*/g" -e "s/"$bashString"ESCAPE/\\\/g" >> tmp5 fi ((nLine++)) ((nLineDisplayed++)) done < tmp3 cp tmp5 book.txt rm tmp3 rm tmp5 else cp tmp3 book.txt rm tmp3 fi countPageTotal=$(($(wc -l < book.txt)/heightPage)) sizeRaw=0 for ((nTable=0; nTable < countTable; nTable++)) do sizeRaw=$((sizeRaw+$(stat -c "%s" ${fileTable[nTable]}))) done if [ $sizeRaw == 0 ] then size="0 byte" elif [ $sizeRaw == 1 ] then size="1 byte" elif (($sizeRaw < 1024)) then size=$sizeRaw" bytes" elif (($sizeRaw < 1048576)) then sizeInteger=$((sizeRaw/1024)) sizeDecimal=$(echo "scale=1;("$sizeRaw/"1024)"-$((sizeRaw/1024)) | bc -l) sizeNumberOne=$(echo "scale=1;"$sizeRaw/"1024" | bc -l) sizeNumberFull=$(echo $sizeRaw/"1024" | bc -l) if [ $sizeDecimal == 0 ] then if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size=$sizeRaw" bytes ("$sizeInteger"KiB)" else size=$sizeRaw" bytes (≈ "$sizeInteger"KiB)" fi else if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size=$sizeRaw" bytes ("$sizeNumberOne"KiB)" else size=$sizeRaw" bytes (≈ "$sizeNumberOne"KiB)" fi fi elif (($sizeRaw < 1073741824)) then sizeInteger=$((sizeRaw/1048576)) sizeDecimal=$(echo "scale=1;("$sizeRaw/"1048576)"-$((sizeRaw/1048576)) | bc -l) sizeNumberOne=$(echo "scale=1;"$sizeRaw/"1048576" | bc -l) sizeNumberFull=$(echo $sizeRaw/"1048576" | bc -l) if [ $sizeDecimal == 0 ] then if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size=$sizeRaw" bytes ("$sizeInteger"MiB)" else size=$sizeRaw" bytes (≈ "$sizeInteger"MiB)" fi else if (( $(echo $sizeNumberOne == $sizeNumberFull | bc -l) )) then size=$sizeRaw" bytes ("$sizeNumberOne"MiB)" else size=$sizeRaw" bytes (≈ "$sizeNumberOne"MiB)" fi fi fi clear echo "Book specifications (book.txt) :" echo "Number of pages = "$countPageTotal if [ $countTable == 1 ] then echo "Total file size = "$size else echo "Total files size = "$size fi read exit 0 Lorsque vous exécutez la routine (.sh), se propose à vous une question "Page width (maximum line length) ?", il vous faut alors renseigner le nombre de caractères souhaités pour la largeur des pages (la police de caractère utilisée doit être à espacement unique) : Par exemple si vous indiquez en largeur de page le nombre 50, vous verrez que le nombre de caractères maximums d'une ligne dans les pages ne dépassera jamais 50, largeur cependant soustraite à la marge de reliure si cette dernière est supérieure à 0 (voir ci-après). Lors des opérations de pagination, une coupure et un retour à la ligne des phrases trop longues sera effectuée (coupure qui s'effectue entre les mots d'une façon naturelle). Ensuite une autre question "Page height (maximum line number per page) ?" apparaît, il convient cette fois-ci de renseigner le nombre de lignes souhaitées pour la hauteur des pages : Si vous indiquez en hauteur de page le nombre 75, le nombre de lignes maximales sur une page de votre livre ne dépassera jamais 75 (numérotation des pages y compris). Puis, la question "Gutter margin (indented text close to the gutter) ?" est posée, vous devez donc renseigner la largeur de la marge (ou retrait) qu'aura le texte par rapport à la reliure du livre : Si vous indiquez en largeur de marge le nombre 10, le texte au bord de la reliure sera éloigné de celle-ci de 10 caractères (marge se situant à gauche pour les pages impaires et à droite pour les pages paires), ce qui facilitera d'autant la lecture et l'ergonomie du livre (indiquez un paramètre égal à 0 si vous ne souhaitez aucune marge). Lorsque vous validez cette troisième question, se propose à vous le choix "Sort option ?". Choisissez "a" (pour "Alphanumeric sort" ou tri alphanumérique) puis validez pour trier automatiquement les fichiers dans l'ordre alphanumérique, ou choisissez "m" (pour "Manual sort" ou tri manuel) pour effectuer vous-même le tri. Si vous avez choisi l'option de tri "m", le menu "Manual sort :" (tri manuel) s'affiche à l'écran suivi de quelques commandes (détaillées ci-dessous), ainsi que l'arborescence des fichiers qui vont être inclus dans le livre, avec successivement le numéro d'apparition des fichiers et le chemin d'accès, c'est à ce moment que vous pouvez modifier l'ordre du tri. Voici ci-dessous le détail des commandes possibles : Détail des commandes du menu "Manual sort :" :
- e (pour "Exit" ou sortie) permet de sortir du menu et commencer la génération du livre.
- s (pour "Save" ou sauvegarde) permet de sauvegarder l'arborescence de fichiers dans un fichier nommé file.tree.
- l (pour "Load" ou chargement) permet de charger le fichier nommé file.tree contenant l'arborescence de fichiers.
- d (pour "Delete" ou suppression) permet de supprimer le fichier nommé file.tree contenant l'arborescence de fichiers.

Utilisez ces options pour sauvegarder l'arborescence avec la commande "s", la modifier en ouvrant manuellement le fichier file.tree avec un éditeur de texte (comme gedit par exemple). Puis rechargez cette arborescence modifiée avec la commande "l", vous verrez les modifications s'afficher dans le menu "File tree :".
Plus bas le menu "File tree :" est affiché, c'est l'arborescence de fichiers : La flèche orientée vers la gauche en suffixe des chemins d'accès aux fichiers indique la position de la destination d'un autre fichier source que vous pouvez choisir de déplacer. Validez pour décaler ce curseur vers une autre destination (c'est-à-dire vers le bas), ou écrivez un nombre, puis validez afin de changer la position dans l'arborescence (l'ordre de tri) du fichier indiqué en source (soit le nombre indiqué) vers la destination (soit le curseur en forme de flèche). Exemple :
- Le curseur est positionné sur le fichier numéro 3, c'est la destination.
- J'indique le numéro 10, c'est la source, puis je valide.
- Le fichier en position 10 (la source) prend la position 3 (la destination), et inversement, le 3 prend la position 10.
Lorsque l'ordre de tri des fichiers correspond à ce que vous souhaitez, indiquez la commande "e" puis validez pour sortir du menu et débuter la génération du livre. La génération du livre commence alors. Elle prend logiquement un temps proportionnel à la complexité de l'arborescence de fichiers utilisée comme source, ainsi que le nombre de lignes et de caractères dans la programmation et l'encodage de chaque fichiers. Une fois la génération du livre terminée, un fichier texte (à texte continu) nommé book.txt est constitué, qui doit être converti en PDF (naturellement aux bonnes dimensions largeur et hauteur) afin d'en apercevoir les différentes pages et la structure de pagination d'une manière cohérente. La génération terminé et le fichier book.txt créé, un bref descriptif du livre est affiché, soit le nombre de pages qu'il contient ainsi que la taille totale des fichiers de votre arborescence, ce qui correspond à la taille totale des fichiers inclus dans le livre, autrement dit, le contenu utile (cette taille exclue donc la table des fichiers à la toute fin du livre qui n'est là que pour faciliter la navigation dans l'ouvrage final imprimé). Maintenant, il suffit de compiler le tout en un fichier .pdf pour ensuite l'envoyer à un imprimeur ! Libre à vous de faire une utilisation de ce petit programme de génération automatique de livre, lorsque vous aurez besoin de sauvegarder vos fichiers sur un autre support que ceux habituellement utilisés dans le monde du numérique !