PROJET AUTOBLOG


®om's blog

Site original : ®om's blog

⇐ retour index

SSHFS inversé (rsshfs)

dimanche 15 juin 2014 à 13:30

SSHFS permet de monter un répertoire d’une machine distance dans l’arborescence locale en utilisant SSH :

sshfs serveur:/répertoire/distant /répertoire/local

Mais comment monter un répertoire local sur une machine distante ?

Une solution simple serait de se connecter en SSH sur la machine distante et d’exécuter la commande sshfs classique.

Mais d’abord, ce n’est pas toujours directement possible : la machine locale peut ne pas être accessible (non adressable) depuis la machine distante. Ça se contourne en créant un tunnel SSH utilisant la redirection de port distante (option -R).

Et surtout, ce n’est pas toujours souhaitable : cela nécessite que la clé privée autorisée sur la machine locale soit connue de la machine distante. Or, dans certains cas, nous ne voulons pas qu’une machine esclave puisse se connecter à notre machine maître.

Reverse SSHFS

En me basant sur la commande donnée en exemple, j’ai donc écrit un petit script Bash (rsshfs, licence GPLv3) qui permet le reverse SSHFS :

(disponible également sur github)

git clone http://git.rom1v.com/rsshfs.git
cd rsshfs
sudo install rsshfs /usr/local/bin

Les paquets sshfs et fuse doivent être installés sur la machine distante (et l’utilisateur doit appartenir au groupe fuse). Le paquet openssh-sftp-server doit être installé sur la machine locale.

Son utilisation se veut similaire à celle de sshfs :

rsshfs /répertoire/local serveur:/répertoire/distant

Comme avec sshfs, /répertoire/distant doit exister sur serveur et doit être vide.

Il est également possible de monter le répertoire en lecture seule :

rsshfs /répertoire/local serveur:/répertoire/distant -o ro

Contrairement à sshfs, étant donné que rsshfs agit comme un serveur, cette commande ne retourne pas tant que le répertoire distant n’est pas démonté.

Pour démonter, dans un autre terminal :

rsshfs -u serveur:/répertoire/distant

Ou plus simplement en pressant Ctrl+C dans le terminal de la commande de montage.

Amélioration

J’ai choisi la facilité en écrivant un script indépendant qui appelle la commande qui-va-bien.

L’idéal serait d’ajouter cette fonctionnalité à sshfs directement.

Compiler un exécutable pour Android

mercredi 19 mars 2014 à 00:39

android

Je vais présenter dans ce billet comment compiler un exécutable ARM pour Android, l’intégrer à un APK et l’utiliser dans une application.

À titre d’exemple, nous allons intégrer un programme natif, udpxy, dans une application minimale de lecture vidéo.

Contexte

Le framework multimédia d’Android ne supporte pas nativement la lecture de flux UDP multicast (1, 2).

Il est possible, pour y parvenir, d’utiliser des lecteurs alternatifs, par exemple basés sur ffmpeg/libav (l’un est un fork de l’autre), tel que libvlc.

Il existe par ailleurs un outil natif, sous licence GPLv3, relayant du trafic UDP multicast vers du HTTP : udpxy. N’importe quel client supportant HTTP (comme le lecteur natif d’Android) peut alors s’y connecter. C’est cet outil que nous allons utiliser ici.

udpxy

Compilation classique

Avant de l’intégrer, comprenons son utilisation en le faisant tourner sur un ordinateur classique (Debian Wheezy 64 bits pour moi).

Il faut d’abord le télécharger les sources, les extraire et compiler :

wget http://www.udpxy.com/download/1_23/udpxy.1.0.23-9-prod.tar.gz
tar xf udpxy.1.0.23-9-prod.tar.gz
cd udpxy-1.0.23-9/
make

Si tout se passe bien, nous obtenons (entre autres) un binaire udpxy.

Test de diffusion

Pour tester, nous avons besoin d’une source UDP multicast. Ça tombe bien, VLC peut la fournir. Pour obtenir le résultat attendu par udpxy, nous devons diffuser vers une adresse multicast (ici 239.0.0.1). Par exemple, à partir d’un fichier MKV :

cvlc video.mkv ':sout=#udp{dst=239.0.0.1:1234}'

En parallèle, démarrons une instance d’udpxy, que nous venons de compiler :

./udpxy -p 8379

Cette commande va démarrer un proxy relayant de l’UDP multicast vers de l’HTTP, écoutant sur le port 8379.

Dans un autre terminal, nous pouvons faire pointer VLC sur le flux ainsi proxifié :

vlc http://localhost:8379/udp/239.0.0.1:1234/

Normalement, le flux doit être lu correctement.

Remarquez qu’udpxy pourrait très bien être démarré sur une autre machine (il suffirait alors de remplacer localhost par son IP). Mais pour la suite, nous souhaiterons justement exécuter udpxy localement sur Android.

Bien sûr, avec VLC, nous n’aurions pas besoin d’udpxy. Le flux est lisible directement avec la commande :

vlc udp://@239.0.0.1:1234/

Android

Notez que certains devices Android ne supportent pas le multicast, la réception de flux multicast ne fonctionnera donc pas.

Maintenant que nous avons vu comment fonctionne udpxy, portons-le sur Android.

Notre but est de le contrôler à partir d’une application et le faire utiliser par le lecteur vidéo natif.

Pour cela, plusieurs étapes sont nécessaires :

  1. obtenir un binaire ARM exécutable pour Android ;
  2. le packager avec une application ;
  3. l’extraire ;
  4. l’exécuter.

Exécutable ARM

Pré-compilé

Pour obtenir un binaire ARM exécutable, le plus simple, c’est évidemment de le récupérer déjà compilé, s’il est disponible (c’est le cas pour udpxy). Dans ce cas, il n’y a rien à faire.

Pour le tester, transférons-le sur le téléphone et exécutons-le :

adb push udpxy /data/local/tmp
adb shell /data/local/tmp/udpxy -p 8379

Si tout se passe bien, cette commande ne produit en apparence rien : elle attend qu’un client se connecte. Pour valider le fonctionnement, si le téléphone est sur le même réseau que votre ordinateur, vous pouvez utiliser cette instance (ARM) d’udpxy comme proxy entre la source multicast et un lecteur VLC local :

vlc http://<ip_device>:8379/udp/239.0.0.1:1234/

Pour obtenir l’ip du téléphone :

adb shell netcfg | grep UP
Compilation ponctuelle

S’il n’est pas disponible, il va falloir le compiler soi-même à partir des sources, ce qui nécessite le NDK Android, fournissant des chaînes de compilation pré-compilées.

Il suffit alors d’initialiser la variable d’environnement CC pour pointer sur la bonne chaîne de compilation (adaptez les chemins et l’architecture selon votre configuration) :

export NDK=~/android/ndk
export SYSROOT="$NDK/platforms/android-19/arch-arm"
export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc --sysroot=$SYSROOT"
make

Bravo, vous venez de générer un binaire udpxy pour l’architecture ARM.

Compilation intégrée

La compilation telle que réalisée ci-dessus est bien adaptée à la génération d’un exécutable une fois de temps en temps, mais s’intègre mal dans un système de build automatisé. En particulier, un utilisateur avec une architecture différente devra adapter les commandes à exécuter.

Heureusement, le NDK permet une compilation plus générique.

Pour cela, il faut créer un répertoire jni dans un projet Android (ou n’importe où d’ailleurs, mais en pratique c’est là qu’il est censé être), y mettre les sources et écrire des Makefiles.

Créons donc un répertoire jni contenant les sources. Vu que nous les avons déjà extraites, copions-les à la racine de jni/ :

cp -rp udpxy-1.0.23-9/ jni/
cd jni/

Créons un Makefile nommé Android.mk :

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := udpxy
LOCAL_SRC_FILES := udpxy.c sloop.c rparse.c util.c prbuf.c ifaddr.c ctx.c mkpg.c \
                   rtp.c uopt.c dpkt.c netop.c extrn.c main.c

include $(BUILD_EXECUTABLE)

Puis compilons :

ndk-build

ndk-build se trouve à la racine du NDK.

Le binaire sera généré dans libs/armeabi/udpxy.

Afin d’organiser les projets plus proprement, il vaut mieux mettre les sources d’udpxy et son Android.mk dans un sous-répertoire spécifique au projet (dans jni/udpxy/). Dans ce cas, il faut rajouter un fichier jni/Android.mk contenant :

include $(call all-subdir-makefiles)

Packager avec l’application

Je suppose ici que vous savez déjà créer une application Android.

Nous devons maintenant intégrer le binaire dans l’APK. Pour cela, il y a principalement deux solutions :

Vu que les projets library ne gèrent pas les assets, nous allons utiliser une ressource raw.

Il faut donc copier le binaire dans res/raw/, à chaque fois qu’il est généré (à automatiser donc).

Extraire l’exécutable

L’exécutable est bien packagé avec l’application, et comme toutes les ressources, nous pouvons facilement obtenir un InputStream (le fonctionnement est similaire pour les assets).

Mais pour l’exécuter en natif, le binaire doit être présent sur le système de fichiers. Il faut donc le copier et lui donner les droits d’exécution. Sans la gestion des exceptions, cela donne :

// "/data/data/<package>/files/udpxy"
File target = new File(getFilesDir(), "udpxy")
InputStream in = getResources().openRawResource(R.raw.udpxy);
OutputStream out = new FileOutputStream(target);
// copy from R.raw.udpxy to /data/data/<package>/files/udpxy
FileUtils.copy(in, out);
// make the file executable
FileUtils.chmod(target, 0755);

Et les parties intéressantes de FileUtils :

public static void copy(InputStream in, OutputStream os) throws IOException {
    byte[] buf = new byte[4096];
    int read;
    while ((read = (in.read(buf))) != -1) {
        os.write(buf, 0, read);
    }
}

public static boolean chmod(File file, int mode) throws IOException {
    String sMode = String.format("%03o", mode); // to string octal value
    String path = file.getAbsolutePath();
    String[] argv = { "chmod", sMode, path };
    try {
        return Runtime.getRuntime().exec(argv).waitFor() == 0;
    } catch (InterruptedException e) {
        throw new IOException(e);
    }
}

Exécuter le programme natif

Maintenant que le binaire est disponible sur le système de fichiers, il suffit de l’exécuter :

String[] command = { udpxyBin.getAbsolutePath(), "-p", "8379" };
udpxyProcess = Runtime.getRuntime().exec(command);

Le lecteur vidéo pourra alors utiliser l’URI proxifié comme source de données :

String src = UdpxyService.proxify("239.0.0.1:1234");

Projets

andudpxy

Je mets à disposition sous licence GPLv3 le projet library andudpxy, qui met en œuvre ce que j’ai expliqué ici :

git clone http://git.rom1v.com/andudpxy.git

(ou sur github)

Pour l’utiliser dans votre application, n’oubliez pas de référencer la library et de déclarer le service UdpxyService dans votre AndroidManifest.xml :

<service android:name="com.rom1v.andudpxy.UdpxyService" />

Pour démarrer le démon :

UdpxyService.startUdpxy(context);

et pour l’arrêter :

UdpxyService.stopUdpxy(context);

andudpxy-sample

J’ai également écrit une application minimale de lecture vidéo qui utilise cette library :

git clone http://git.rom1v.com/andudpxy-sample.git

(ou sur github)

C’est toujours utile d’avoir une application d’exemple censée fonctionner 😉

L’adresse du flux UDP multicast à lire est écrite en dur dans MainActivity (et le flux doit fonctionner lors du démarrage de l’activité) :

private static final String ADDR = "239.0.0.1:1234";

Compilation

Après avoir cloné les 2 projets dans un même répertoire parent, renommez les local.properties.sample en local.properties, éditez-les pour indiquer le chemin du SDK et du NDK.

Ensuite, allez dans le répertoire andudpxy-sample, puis exécutez :

ant clean debug

Vous devriez obtenir bin/andudpxy-sample-debug.apk.

Bien sûr, vous pouvez aussi les importer dans Eclipse (ou un autre IDE) et les compiler selon vos habitudes.

Conclusion

Nous avons réussi à compiler et exécuter un binaire ARM sur Android, packagé dans une application.

Ceci peut être utile pour exécuter du code déjà implémenté nativement pour d’autres plates-formes, pour faire tourner un démon natif… Par exemple, le projet Serval (sur lequel j’ai un peu travaillé) utilise un démon servald, qui tourne également sur d’autres architectures.

Ce n’est cependant pas la seule manière d’exécuter du code natif dans une application : la plus courante est d’appeler des fonctions natives (et non un exécutable) directement à partir de Java, en utilisant JNI. L’une et l’autre répondent à des besoins différents.

Compiler un exécutable pour Android

mardi 18 mars 2014 à 23:39

Je vais présenter dans ce billet comment compiler un exécutable ARM pour Android, l’intégrer à un APK et l’utiliser dans une application.

À titre d’exemple, nous allons intégrer un programme natif, udpxy, dans une application minimale de lecture vidéo.

Contexte

Le framework multimédia d’Android ne supporte pas nativement la lecture de flux UDP multicast (1, 2).

Il est possible, pour y parvenir, d’utiliser des lecteurs alternatifs, par exemple basés sur ffmpeg/libav (l’un est un fork de l’autre) ou libvlc.

Il existe par ailleurs un outil natif, sous licence GPLv3, relayant du trafic UDP multicast vers du HTTP : udpxy. N’importe quel client supportant HTTP (comme le lecteur natif d’Android) peut alors s’y connecter. C’est cet outil que nous allons utiliser ici.

udpxy

Compilation classique

Avant de l’intégrer, comprenons son utilisation en le faisant tourner sur un ordinateur classique (Debian Wheezy 64 bits pour moi).

Il faut d’abord le télécharger les sources, les extraire et compiler :

wget http://www.udpxy.com/download/1_23/udpxy.1.0.23-9-prod.tar.gz
tar xf udpxy.1.0.23-9-prod.tar.gz
cd udpxy-1.0.23-9/
make

Si tout se passe bien, nous obtenons (entre autres) un binaire udpxy.

Test de diffusion

Pour tester, nous avons besoin d’une source UDP multicast. Ça tombe bien, VLC peut la fournir. Pour obtenir le résultat attendu par udpxy, nous devons diffuser vers une adresse multicast (ici 239.0.0.1). Par exemple, à partir d’un fichier MKV :

cvlc video.mkv ':sout=#udp{dst=239.0.0.1:1234}'

En parallèle, démarrons une instance d’udpxy, que nous venons de compiler :

./udpxy -p 8379

Cette commande va démarrer un proxy relayant de l’UDP multicast vers de l’HTTP, écoutant sur le port 8379.

Dans un autre terminal, nous pouvons faire pointer VLC sur le flux ainsi proxifié :

vlc http://localhost:8379/udp/239.0.0.1:1234/

Normalement, le flux doit être lu correctement.

Remarquez qu’udpxy pourrait très bien être démarré sur une autre machine (il suffirait alors de remplacer localhost par son IP). Mais pour la suite, nous souhaiterons justement exécuter udpxy localement sur Android.

Bien sûr, avec VLC, nous n’aurions pas besoin d’udpxy. Le flux est lisible directement avec la commande :

vlc udp://@239.0.0.1:1234/

Android

Notez que certains devices Android ne supportent pas le multicast, la réception de flux multicast ne fonctionnera donc pas.

Maintenant que nous avons vu comment fonctionne udpxy, portons-le sur Android.

Notre but est de le contrôler à partir d’une application et le faire utiliser par le lecteur vidéo natif.

Pour cela, plusieurs étapes sont nécessaires :

  1. obtenir un binaire ARM exécutable pour Android ;
  2. le packager avec une application ;
  3. l’extraire ;
  4. l’exécuter.

Exécutable ARM

Pré-compilé

Pour obtenir un binaire ARM exécutable, le plus simple, c’est évidemment de le récupérer déjà compilé, s’il est disponible (c’est le cas pour udpxy). Dans ce cas, il n’y a rien à faire.

Pour le tester, transférons-le sur le téléphone et exécutons-le :

adb push udpxy /data/local/tmp
adb shell /data/local/tmp/udpxy -p 8379

Si tout se passe bien, cette commande ne produit en apparence rien : elle attend qu’un client se connecte. Pour valider le fonctionnement, si le téléphone est sur le même réseau que votre ordinateur, vous pouvez utiliser cette instance (ARM) d’udpxy comme proxy entre la source multicast et un lecteur VLC local :

vlc http://xx.xx.xx.xx:8379/udp/239.0.0.1:1234/

Replacer xx.xx.xx.xx par l’ip du device, qu’il est possible d’obtenir ainsi :

adb shell netcfg | grep UP

Compilation ponctuelle

S’il n’est pas disponible, il va falloir le compiler soi-même à partir des sources, ce qui nécessite le NDK Android, fournissant des chaînes de compilation pré-compilées.

Il suffit alors d’initialiser la variable d’environnement CC pour pointer sur la bonne chaîne de compilation (adaptez les chemins et l’architecture selon votre configuration) :

export NDK=~/android/ndk
export SYSROOT="$NDK/platforms/android-19/arch-arm"
export CC="$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc --sysroot=$SYSROOT"
make

Bravo, vous venez de générer un binaire udpxy pour l’architecture ARM.

Compilation intégrée

La compilation telle que réalisée ci-dessus est bien adaptée à la génération d’un exécutable une fois de temps en temps, mais s’intègre mal dans un système de build automatisé. En particulier, un utilisateur avec une architecture différente devra adapter les commandes à exécuter.

Heureusement, le NDK permet une compilation plus générique.

Pour cela, il faut créer un répertoire jni dans un projet Android (ou n’importe où d’ailleurs, mais en pratique c’est là qu’il est censé être), y mettre les sources et écrire des Makefiles.

Créons donc un répertoire jni contenant les sources. Vu que nous les avons déjà extraites, copions-les à la racine de jni/ :

cp -rp udpxy-1.0.23-9/ jni/
cd jni/

Créons un Makefile nommé Android.mk :

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := udpxy
LOCAL_SRC_FILES := udpxy.c sloop.c rparse.c util.c prbuf.c ifaddr.c ctx.c \
                   mkpg.c rtp.c uopt.c dpkt.c netop.c extrn.c main.c

include $(BUILD_EXECUTABLE)

Puis compilons :

ndk-build

ndk-build se trouve à la racine du NDK.

Le binaire sera généré dans libs/armeabi/udpxy.

Afin d’organiser les projets plus proprement, il vaut mieux mettre les sources d’udpxy et son Android.mk dans un sous-répertoire spécifique au projet (dans jni/udpxy/). Dans ce cas, il faut rajouter un fichier jni/Android.mk contenant :

include $(call all-subdir-makefiles)

Packager avec l’application

Je suppose ici que vous savez déjà créer une application Android.

Nous devons maintenant intégrer le binaire dans l’APK. Pour cela, il y a principalement deux solutions :

Vu que les projets library ne gèrent pas les assets, nous allons utiliser une ressource raw.

Il faut donc copier le binaire dans res/raw/, à chaque fois qu’il est généré (à automatiser donc).

Extraire l’exécutable

L’exécutable est bien packagé avec l’application, et comme toutes les ressources, nous pouvons facilement obtenir un InputStream (le fonctionnement est similaire pour les assets).

Mais pour l’exécuter en natif, le binaire doit être présent sur le système de fichiers. Il faut donc le copier et lui donner les droits d’exécution. Sans la gestion des exceptions, cela donne :

// "/data/data/<package>/files/udpxy"
File target = new File(getFilesDir(), "udpxy")
InputStream in = getResources().openRawResource(R.raw.udpxy);
OutputStream out = new FileOutputStream(target);
// copy from R.raw.udpxy to /data/data/<package>/files/udpxy
FileUtils.copy(in, out);
// make the file executable
FileUtils.chmod(target, 0755);

Et les parties intéressantes de FileUtils :

public static void copy(InputStream in, OutputStream os) throws IOException {
    byte[] buf = new byte[4096];
    int read;
    while ((read = (in.read(buf))) != -1) {
        os.write(buf, 0, read);
    }
}

public static boolean chmod(File file, int mode) throws IOException {
    String sMode = String.format("%03o", mode); // to string octal value
    String path = file.getAbsolutePath();
    String[] argv = { "chmod", sMode, path };
    try {
        return Runtime.getRuntime().exec(argv).waitFor() == 0;
    } catch (InterruptedException e) {
        throw new IOException(e);
    }
}

Exécuter le programme natif

Maintenant que le binaire est disponible sur le système de fichiers, il suffit de l’exécuter :

String[] command = { udpxyBin.getAbsolutePath(), "-p", "8379" };
udpxyProcess = Runtime.getRuntime().exec(command);

Le lecteur vidéo pourra alors utiliser l’URI proxifié comme source de données :

String src = UdpxyService.proxify("239.0.0.1:1234");

Projets

andudpxy

Je mets à disposition sous licence GPLv3 le projet library andudpxy, qui met en œuvre ce que j’ai expliqué ici :

git clone http://git.rom1v.com/andudpxy.git

(ou sur github)

Pour l’utiliser dans votre application, n’oubliez pas de référencer la library et de déclarer le service UdpxyService dans votre AndroidManifest.xml :

<service android:name="com.rom1v.andudpxy.UdpxyService" />

Pour démarrer le démon :

UdpxyService.startUdpxy(context);

et pour l’arrêter :

UdpxyService.stopUdpxy(context);

andudpxy-sample

J’ai également écrit une application minimale de lecture vidéo qui utilise cette library :

git clone http://git.rom1v.com/andudpxy-sample.git

(ou sur github)

C’est toujours utile d’avoir une application d’exemple censée fonctionner ;-)

L’adresse du flux UDP multicast à lire est écrite en dur dans MainActivity (et le flux doit fonctionner lors du démarrage de l’activité) :

private static final String ADDR = "239.0.0.1:1234";

Compilation

Après avoir cloné les 2 projets dans un même répertoire parent, renommez les local.properties.sample en local.properties, éditez-les pour indiquer le chemin du SDK et du NDK.

Ensuite, allez dans le répertoire andudpxy-sample, puis exécutez :

ant clean debug

Vous devriez obtenir bin/andudpxy-sample-debug.apk.

Bien sûr, vous pouvez aussi les importer dans Eclipse (ou un autre IDE) et les compiler selon vos habitudes.

Conclusion

Nous avons réussi à compiler et exécuter un binaire ARM sur Android, packagé dans une application.

Ceci peut être utile pour exécuter du code déjà implémenté nativement pour d’autres plates-formes, pour faire tourner un démon natif… Par exemple, le projet Serval (sur lequel j’ai un peu travaillé) utilise un démon servald, qui tourne également sur d’autres architectures.

Ce n’est cependant pas la seule manière d’exécuter du code natif dans une application : la plus courante est d’appeler des fonctions natives (et non un exécutable) directement à partir de Java, en utilisant JNI. L’une et l’autre répondent à des besoins différents.

Des slides Beamer en Markdown

samedi 15 février 2014 à 19:29

Pour produire des slides propres pour une présentation, j’aime beaucoup Beamer (basé sur LaTeX). Mais la syntaxe est un peu lourde et la configuration est parfois inutilement compliquée (fonts, encodage, compilation multipasse…).

Est-il possible d’avoir les avantages de Beamer sans ses inconvénients ? La réponse est oui, grâce à pandoc et son Markdown étendu.

Beamer

Voici le code d’un exemple très simple de présentation Beamer :

\documentclass[hyperref={pdfpagelabels=false}]{beamer}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}

\title{Exemple}
\author{Romain Vimont}
\date{15 février 2014}

\begin{document}

\begin{frame}
\titlepage
\end{frame}

\section{Ma section}

\begin{frame}{Ma première frame}
\begin{itemize}
 \item c'est bien
 \item mais c'est verbeux
\end{itemize}
\end{frame}

\end{document}

Le code source est, il faut bien l’avouer, assez rebutant, et le rapport signal/bruit assez faible.

Une fois les paquets pdflatex, textlive-latex-base et latex-beamer installés (sous Debian), vous pouvez le compiler avec :

pdflatex fichier.tex

Markdown

Voici maintenant l’équivalent en Pandoc-Markdown :

% Exemple
% Romain Vimont
% 15 février 2014

# Ma section

## Ma première frame

 - c'est bien
 - et en plus ce n'est pas verbeux

Indiscutablement, c’est beaucoup plus lisible !

Avec le paquet pandoc (en plus des paquets latex déjà installés), vous pouvez le compiler avec :

pandoc -st beamer fichier.md -o fichier.pdf

Notez que le résultat n’est pas strictement identique, la version compilée avec pandoc ajoute une frame de section, mais il ne s’agit que d’une différence de template par défaut.

Démo

J’ai créé une présentation d’exemple avec un thème personnalisé.

Le résultat est disponible ici, mais c’est surtout la source (raw) qui est intéressante. Pour récupérer le projet et générer le pdf :

git clone http://git.rom1v.com/mdbeamer.git
cd mdbeamer
make

Il est également disponible sur github.

Ce projet a vocation à être utilisé comme base pour mes futures présentations (et les vôtres si vous le désirez). Chacune d’entre elles sera sur une branche git et sur un remote différents.

Injection de version

Pour pouvoir distinguer rapidement différentes versions d’une même présentation, j’ai également ajouté au Makefile une commande pour injecter un identifiant de version à côté de la date (donc à la fin de la 3e ligne du code source). Il s’agit du résultat de git describe (contenant le nom du dernier tag annoté) ou à défaut simplement le numéro de commit actuel.

Pour l’utiliser :

make withversion

Un format pivot

J’utilise ici le Pandoc-Markdown pour écrire du Beamer plus simplement.

Mais son intérêt est beaucoup plus général : il s’agit d’un véritable format pivot, compilable vers de nombreux formats.

Pour de la documentation par exemple, il suffit de l’écrire en Pandoc-Markdown et de la compiler, grâce à pandoc, en :

C’est d’ailleurs très pratique quand quelqu’un vous demande une documentation dans un format totalement inadapté (type docx), à rédiger de manière collaborative : il suffit alors d’utiliser Pandoc-Markdown, git et un Makefile.

Pour les slides, pandoc supporte, en plus de Beamer, la compilation vers des slides HTML :

Cette généricité a bien sûr des limites : l’utilisation de code spécifique à un format particulier (tel que j’en utilise dans mon exemple) empêche de le compiler correctement vers d’autres formats.

Conclusion

Le language Markdown (étendu par pandoc) permet de combiner la généricité, la simplicité et la gitabilité pour écrire des documents ou des slides, ce qui en fait un outil absolument indispensable.

Des slides Beamer en Markdown

samedi 15 février 2014 à 18:29

Pour produire des slides propres pour une présentation, j’aime beaucoup Beamer (basé sur LaTeX). Mais la syntaxe est un peu lourde et la configuration est parfois inutilement compliquée (fonts, encodage, compilation multipasse…).

Est-il possible d’avoir les avantages de Beamer sans ses inconvénients ? La réponse est oui, grâce à pandoc et son Markdown étendu.

Beamer

Voici le code d’un exemple très simple de présentation Beamer :

\documentclass[hyperref={pdfpagelabels=false}]{beamer}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}

\title{Exemple}
\author{Romain Vimont}
\date{15 février 2014}

\begin{document}

\begin{frame}
\titlepage
\end{frame}

\section{Ma section}

\begin{frame}{Ma première frame}
\begin{itemize}
 \item c'est bien
 \item mais c'est verbeux
\end{itemize}
\end{frame}

\end{document}

Le code source est, il faut bien l’avouer, assez rebutant, et le rapport signal/bruit assez faible.

Une fois les paquets pdflatex, textlive-latex-base et latex-beamer installés (sous Debian), vous pouvez le compiler avec :

pdflatex fichier.tex

Markdown

Voici maintenant l’équivalent en Pandoc-Markdown :

% Exemple
% Romain Vimont
% 15 février 2014

# Ma section

## Ma première frame

 - c'est bien
 - et en plus ce n'est pas verbeux

Indiscutablement, c’est beaucoup plus lisible !

Avec le paquet pandoc (en plus des paquets latex déjà installés), vous pouvez le compiler avec :

pandoc -st beamer fichier.md -o fichier.pdf

Notez que le résultat n’est pas strictement identique, la version compilée avec pandoc ajoute une frame de section, mais il ne s’agit que d’une différence de template par défaut.

Démo

J’ai créé une présentation d’exemple avec un thème personnalisé.

beamer

Le résultat est disponible ici, mais c’est surtout la source (raw) qui est intéressante. Pour récupérer le projet et générer le pdf :

git clone http://git.rom1v.com/mdbeamer.git
cd mdbeamer
make

Il est également disponible sur github.

Ce projet a vocation à être utilisé comme base pour mes futures présentations (et les vôtres si vous le désirez). Chacune d’entre elles sera sur une branche git et sur un remote différents.

Injection de version

Pour pouvoir distinguer rapidement différentes versions d’une même présentation, j’ai également ajouté au Makefile une commande pour injecter un identifiant de version à côté de la date (donc à la fin de la 3e ligne du code source). Il s’agit du résultat de git describe (contenant le nom du dernier tag annoté) ou à défaut simplement le numéro de commit actuel.

Pour l’utiliser :

make withversion

Un format pivot

J’utilise ici le Pandoc-Markdown pour écrire du Beamer plus simplement.

Mais son intérêt est beaucoup plus général : il s’agit d’un véritable format pivot, compilable vers de nombreux formats.

Pour de la documentation par exemple, il suffit de l’écrire en Pandoc-Markdown et de la compiler, grâce à pandoc, en :

C’est d’ailleurs très pratique quand quelqu’un vous demande une documentation dans un format totalement inadapté (type docx), à rédiger de manière collaborative : il suffit alors d’utiliser Pandoc-Markdown, git et un Makefile.

Pour les slides, pandoc supporte, en plus de Beamer, la compilation vers des slides HTML :

Cette généricité a bien sûr des limites : l’utilisation de code spécifique à un format particulier (tel que j’en utilise dans mon exemple) empêche de le compiler correctement vers d’autres formats.

Conclusion

Le language Markdown (étendu par pandoc) permet de combiner la généricité, la simplicité et la _git_abilité pour écrire des documents ou des slides, ce qui en fait un outil absolument indispensable.