Lecture de documents à haute voix

armel35

Membre confirmé
10 Octobre 2008
46
3
Bonjour,

Gautier Delorme a publié un script via automator qui permet de lire à haute voix, en français, des documents pdf, doc, rtf ou txt, et ce juste en cliquant sur le nom du fichier dans le finder.

http://www.gautierdelorme.com/104-l...haute-sur-mac-os-x-10-9-mavericks#comment-876

J'ai installé xcode, la ligne de commandes de xcode, un package pdftotext ( http://www.bluem.net/files/pdftotext.dmg ), mis les bons chemins dans le .bath_profile, mais je n'arrive pas à faire fonctionner le service (clic droit sur un fichier txt par exemple), rien ne se passe.

Curieusement, quand j'utilise le terminal et tape 'Lire un fichier.txt/doc/rtf, tout fonctionne, et dans le cas d'un fichier pdf cela fonctionne quand il ne contient pas de caractères accentués, sinon j'ai un message d'erreur non UT-F8

Quelqu'un connait-il ce script ? aurait une idée sur les pré-requis que l'auteur aurait oublié de préciser ?
Je pense notamment à son chemin /opt/local/bin/pdftotext : je n'ai pas trouvé le programme qui aurait un tel chemin, ceux que j'ai trouvés installent pdftotext vers usr/local/bin

D'avance merci,


Armel
 
Bonjour,

Info : Xcode n'est pas nécessaire pour installer pdftotext, il suffit d'ouvrir le paquet "Installer.pkg" avec le menu contextuel "Ouvrir avec" --> "Programme d'installation (par défaut)" pour l'installer, cela exige un mot de passe administrateur, l'installeur placera pdftotext dans le dossier /usr/local/bin/ automatiquement.

Il y a plusieurs problèmes avec ce script.
1- Ce script ne vérifie pas l'existence d'un fichier quand il crée un fichier temporaire, donc, il peut supprimer un fichier existant (par exemple : j'ai un fichier "abc.pdf" et un fichier "abc.txt" dans le même dossier, lorsque que j'exécute le script avec le fichier "abc.pdf", il supprime le fichier "abc.txt").

2- il est inutile d'écrire le résultat de pdftotext ou textutil dans un fichier temporaire, car on peut obtenir le résultat dans le sdout avec l'option - a la fin de la commande pdftotext ou l'option -stdout pour la commande textutil.

3-Le script ne fonctionne pas avec les espaces dans les noms de dossier ou de fichier, il faut protéger les variables par des "" (ex : "$1" au lieu de $1

4- Les fichiers ".txt" ne fonctionne pas plus (si il y a des accents) quand l'encodage de ces fichiers sont en MacRoman par exemple, donc il faut utiliser la commande textutil aussi sur les fichiers ".txt", l'encodage de la sortie de textutil est UTF8 par défaut.

5- Pour le problème des accents dans les PDF, il faut spécifier l'encodage de sortie de la commande pdftotext (c'est l'option -enc UTF-8)

Voici le script modifié :

Bloc de code:
#!/bin/bash
if test $# -ne 1 || test ! -f "$1"; then echo "Vous avez oublié le nom du fichier à lire (ou il n'existe pas)."; exit 0; fi
if [ ${1##*.} = pdf ]
then
    pdftotext -enc UTF-8 "$1" - | say
else
    textutil -convert txt -stdout "$1" | say
fi


--
Pour Automator : cela ne fonctionne pas car les données en entrée doit-être "Comme arguments" au lieu de "stdin" dans le popup menu.
Utilise ce script directement dans l'action au lieu d'utiliser le fichier (ton script shell), comme cela le script pourra lire un ou plusieurs fichiers.

Bloc de code:
for f in "$@"
do
    if test -f "$f"
    then
        if [ ${f##*.} = pdf ]
        then
            /usr/local/bin/pdftotext -enc UTF-8 "$f" - | say
        else
            textutil -convert txt -stdout "$f" | say
        fi
    fi
done

Important : remplace /usr/local/bin/ par le chemin de ton exécutable pdftotext, pour connaitre le chemin, tape ceci dans le Terminal
Bloc de code:
which pdftotext
Cela fonctionne chez-moi sans le chemin complet de pdftotext, mais ce n'est pas le cas pour tout le monde.
 
  • J’aime
Réactions: armel35
Merci beaucoup JacqR, tout a fonctionné du premier coup grâce à tes indications !
 
Bonjour,

Info : Xcode n'est pas nécessaire pour installer pdftotext, il suffit d'ouvrir le paquet "Installer.pkg" avec le menu contextuel "Ouvrir avec" --> "Programme d'installation (par défaut)" pour l'installer, cela exige un mot de passe administrateur, l'installeur placera pdftotext dans le dossier /usr/local/bin/ automatiquement.

Dans ma joie, j'ai oublié de te demander où trouver "installer.pkg" ?
Fais-tu référence à l'installation de la ligne de commande préconisée par Gautier Delorme ?

"Si la commande pdftotext ne fonctionne pas, c’est sûrement parce que vous n’avez pas installé les outils de ligne de commande de XCode via cette commande (pensez aussi à vérifier votre PATH, le chemin de la commande est /opt/local/bin/) :

xcode-select --install"
 
Bonjour,

Dans ma joie, j'ai oublié de te demander où trouver "installer.pkg" ?
Fais-tu référence à l'installation de la ligne de commande préconisée par Gautier Delorme ?
Oui, vous avez besoin de Xcode si vous voulez utiliser la commande xcode-select --install.

Mais cela n'est pas nécessaire, il suffit de monter le fichier "pdftotext.dmg", de faire un double-clic sur le fichier "Installer.pkg" dans le volume monté pour que le programme d'installation standard de l'OS l'installe, seul inconvénient sur les versions récentes de l'OS, il refusera car cela ne provient pas de l'App Store', donc il faut faire un clic-droit sur le fichier "Installer.pkg" et sélectionner le menu "Ouvrir avec" --> "Programme d'installation (par défaut)"
 
  • J’aime
Réactions: armel35
JacqR,

Sans vouloir abuser de ton temps : comment faire pour arrêter le script quand le fichier est très long ?

Penses-tu que ce soit possible de générer une petite fenêtre de dialogue offrant les possibilités Pause/reprise ou arrêt lecture ?

D'avance, merci pour ta répônse


Didier
 
Bonjour,

Il n'y a pas de solution pour mettre en pause, car la commande say n'accepte pas de se mettre en pause, alors qu'on peut le faire sur des scripts avec pkill -STOP "id du processus" pour mettre en pause et pkill -CONT "id du processus" pour la reprise.
Peut-être que c'est possible en programmant une application Cocoa (je ne sais pas).
Sinon seul VoiceOver en standard sur l'os peut faire cela, cela implique qu'il faut ouvrir le fichier dans une fenêtre d'une application.
Aussi, c'est possible avec la commande say d'enregistrer la parole dans un fichier audio, mais cela est très demandant pour les processeurs et c'est très long quand la taille du fichier texte est grande, après il faut l'ouvrir dans un lecteur audio dont on peut contrôler la pause etc..

--
Donc, il reste l'arrêt du script :
Si vous avez lancé la commande dans le Terminal, il suffit de presser les touches Contrôle et c ou de fermer la fenêtre pour arrêter la lecture.

Pour Automator :
Voici le script bash qui utilise l'application Python pour afficher un bouton qui arrêtera la lecture du fichier
Bash:
scriptPid=$$
function pcall_python() {
PYTHON_ARG="$1" /usr/bin/python - <<END
# -*- coding: utf-8 -*-
import os, sys, Tkinter, subprocess
def killP():
    subprocess.check_call(['/usr/bin/pkill', '-P', os.environ['PYTHON_ARG']]); f.destroy()
f=Tkinter.Tk(); f.title('Lire (service Automator)'); btn=Tkinter.Button(f, text='Arrêter la lecture', width=25, command=killP); btn.pack()
f.call('wm', 'attributes', '.', '-topmost', '1'); f.mainloop()
END
}
b=true
for f in "$@"
do
    if test -f "$f"
    then
        if $b; then ## python n'est pas lancé
            pcall_python "$scriptPid" & ## on lance l'application Python pour afficher le dialogue
            bashpythonPid=$!
        fi
        if [ ${f##*.} = pdf ]
        then
            /usr/local/bin/pdftotext -enc UTF-8 "$f" - | say
        else
            textutil -convert txt -stdout "$f" | say
        fi
        subP=$(pgrep -P "$bashpythonPid") ## on récupère le PID de l'application Python, s'il n'est pas fermé
        b=true; if [ ! -z "$subP" ]; then ps -Axcro pid,command | grep -q " $subP python" && b=false; fi ## si le dialogue est toujours affiché, on ne lancera pas l'app Python au prochain fichier
    fi
done
if ! $b; then /bin/kill $subP > /dev/null 2>&1; fi ## on quitte l'application Python, car il est encore ouvert

Info sur le script :
Si vous pressez le bouton "Arrêter la lecture", Python utilise la commande /usr/bin/pkill -P pour quitter tous les sous-processus en cours du script bash, f.destroy() : cela ferme la fenêtre et quitte l'application Python
Si vous fermez la fenêtre de l'application Python ou vous quittez l'application Python cela ne fera rien, le script continuera normalement.​
 
  • J’aime
Réactions: armel35
Merci beaucoup !


Cette fois encore, tout a fonctionné au premier clic !

Didier
 
Bonjour,

JacqR,
Penses-tu que ce soit possible de générer une petite fenêtre de dialogue offrant les possibilités Pause/reprise ou arrêt lecture ?

Didier
Oui, cela est possible avec le framework (cocoa) NSSpeechSynthesizer.
J’ai trouvé en faisant une recherche pour récupérer les voix disponibles de l’utilisateur par script.

Voici le script bash, il suffit de remplacer l’ancien par celui-ci, il n’y a pas d’autre changement à faire.
Bash:
function pcall_python() {
PYTHON_ARG="$1" /usr/bin/python - <<END
# -*- coding: utf-8 -*-
import os, Tkinter
from AppKit import NSSpeechSynthesizer, NSSpeechWordBoundary
from Foundation import NSObject

def pauseResume():
    if (parole.isSpeaking()):
        btn.config(text='Reprendre la lecture')
        parole.pauseSpeakingAtBoundary_(NSSpeechWordBoundary)
    else:
        if b:
            parole.startSpeakingString_(' ')
            return
        btn.config(text='Pause')
        parole.continueSpeaking()

def stopParole():
    parole.stopSpeaking
    f.destroy()

class SpeechSynthDelegate(NSObject):
    def speechSynthesizer_didFinishSpeaking_(self, synthesizer, success):
        f.destroy()
    def speechSynthesizer_willSpeakWord_ofString_(self, synthesizer, aRange, tText):
        global b
        z = (aRange.location + aRange.length) - len(tText)
        if z==0: b=True

b=False
parole = NSSpeechSynthesizer.alloc().init()
delegate_ = SpeechSynthDelegate.alloc().init();
f=Tkinter.Tk(); f.title('Lire (service Automator)'); btn=Tkinter.Button(f, text='Pause', width=25, command=pauseResume); btn.pack()
btn2=Tkinter.Button(f, text='Arrêter la lecture', width=25, command=stopParole); btn2.pack()
f.call('wm', 'attributes', '.', '-topmost', '1')
parole.setDelegate_(delegate_)
parole.startSpeakingString_(os.environ['PYTHON_ARG'])
f.mainloop()
END
}
for f in "$@"
do
    if test -f "$f"
    then
        if [ ${f##*.} = pdf ]
        then
            pcall_python "$(/usr/local/bin/pdftotext -enc UTF-8 "$f" -)"
        else
            pcall_python "$(textutil -convert txt -stdout "$f")"
        fi
    fi
done




Comme c’est le script python qui part la lecture du texte, fermer la fenêtre ou quitter l’application "Python" fait exactement la même chose que presser le bouton "Arrêter la lecture".