Manuel Reif
  • Home
  • CV
  • R-Packages
  • Blog
  • Impressum

On this page

  • Folien, Folien, Folien
  • Rendern
  • pdfs erstellen
  • pdf handout
  • Ein Skript um alles zu builden
  • Platzierung des Python Skripts

Presentation pipeline

quarto
bash
Linux
Python
Wie aus Quarto Präsentationen mit revealjs automatisiert pdfs und Handouts werden.
Author

Manuel

Published

February 3, 2024

Folien, Folien, Folien

Soll ich lesen?
  1. Erstellst du Folien mit reveal-js mittels quarto?
  2. Willst du .pdf Versionen und .pdf Handouts?
  3. Arbeitest du mit Linux/Ubuntu.
    → Dann Lesebefehl!

Bildbeschreibung

In meiner Lehrveranstaltung zu “Forschungsmethoden und Statistik” habe ich meine Folien mithilfe von quarto und revealjs erstellt. Das war natürlich nicht immer so.
Früher, in der guten alten Zeit, habe ich wie jeder vernünftige Mensch meine Folien mit LaTeX beamer erstellt. 2023 sprach allerdings kaum mehr etwas dafür beamer zu verwenden, denn alle Präsentation mit revealjs sahen einfach gut aus – und das quasi “Out of the box”. Wer will nicht dass seine Folien geil aussehen? Also musste ich einen neuen Workflow aufbauen. Die quarto Dokumente schreibe ich in RStudio und zur schnellen Ansicht rendere ich auch direkt in RStudio. Das Ergebnis sind .html files. Ich brauche aber ebenso Handouts für meine Studierenden und hätte selbst gerne noch eine .pdf Version meiner Präsentation falls alle Browser streiken und Gott bewahre, etwas ähnliches ist mir bereits passiert. Üblicherweise trage ich meine files auf einem USB Stick herum. USB-Sticks bleiben gerne mal wo liegen oder stecken und daher wäre es gut, ein vernünftiges Backup in der Cloud zu haben, um im Falle des Falles auch so auf die Präsentation zugreifen zu können.

Dieser Post wird sich damit beschäftigen, einen für mich funktionierenden automatisierten Ablauf für meine quarto Präsentationen zu erstellen. Folgender Ablauf ist geplant:

  1. Render meine quarto Präsentationen
  2. Erstelle pdfs aus diesen
  3. Erstelle pdf Handouts
  4. Erstelle ein Backup auf google drive

Bisher hatte ich für solche Aufgaben Makefiles1. Damit bin ich nicht so schlecht gefahren – allerdings musste ich die Makefiles immer wieder anpassen bzw. in einen neuen Ordner kopieren wenn eine neue Präsentation erstellt wird. Außerdem sagt mir jeder ich muss mehr in Python machen. Ok, wenn ich das Ganze also in Python machen soll, dann will ich auch folgende Wohlfühl-Features haben:

  1. Multiple .qmd Files sollen parallel gerendert etc. werden2
  2. Das Herumkopieren find ich nicht so prickelnd – daher wäre es toll wenn ich den Prozess direkt aus der bash triggern könnte.
  3. Naja und es soll in Python passieren.

Rendern

Das ist vermutlich der einfachste Part.
Man muss nur sicherstellen quarto installiert zu haben. Am besten man holt sich das neueste RStudio, dann hat man quarto bereits an Board, oder man installiert es sich separat3.

Man kann nun:

  1. RStudio verwenden um das .qmd zu schreiben und auf den “render” Button drücken
  2. Was viel cooler ist: in der bash4 rendern.
Terminal
quarto render FILE.qmd

In der bash rendern ist nicht nur cooler, sondern auch der erste Schritt um das ganze Prozedere zu automatisieren. So far so good. Die pdfs zu erstellen wird ein wenig schwieriger.

pdfs erstellen

Ich hab viel Zeit mit Google verbracht um dieses Problem zu lösen. Ziel ist es .html slides automatisch zu einem .pdf zu konvertieren. Decktape ist dafür ein wirklich ausgezeichnetes tool. Für decktape gibt es auch ein Dockerimage5 → man muss sich keine Gedanken darüber machen, welche obskure andere Software man noch installieren muss, weil alles schon mit dem Dockercontainer verschifft wird und zwar genau in den sicheren Hafen unseres Computers.

Also folgende To-Dos:

  1. Docker holen
  2. Docker image pullen
  3. Docker image verwenden → das ist genau was wir jetzt machen werden

Um jetzt unsere .html Präsentation in .pdf Folien zu verwandeln übergebe ich den Folder in dem die Dateien liegen an den Docker-Container. Das Ganze ist wieder nur eine einzige Zeile und funktioniert folgendermaßen:

Terminal
docker run --rm -t -v path/to/folder:/slides astefanutti/decktape --size='2048x1536' reveal FILE.html FILE.pdf

Was passiert hier?

  1. docker run: okay, wir wollen irgendwas in docker machen, also soll das Image das später genannt wird (astefanutti/decktape) laufen
  2. --rm: es wird saubergemacht nachdem docker fertig ist
  3. -t: damit sehen wir was passiert (stdin and stdout werden attached)
  4. -v: wir wollen was mounten und zwar: ordner/auf/meinem/computer:/container. Decktape rechtnet damit, dass das .html file im Ordner “slides” liegt, daher wird der Ordner vom eigenen Rechner dort reingemountet.
  5. --size='2048x1536': so funktioniert es bei mir. Ich habe einige Einstellungen probiert – es läuft.
  6. reveal offensichtlich arbeiten wir mit reveal-js.
  7. FILE.html FILE.pdf hier dürfen wir noch einen Namen vergeben für das .pdf!

Das Ergebnis ist FILE.pdf, eine pdf Verison der html Präsentation.

pdf handout

Wir wollen Handouts erstellen, die 4 Folien auf einer Seite darstellen, verwenden wir pdfjam. Das geht ratz-fatz. Kaum hat man auf Enter gedrückt ist das file auch schon da. Wir brauchen wieder nur eine Zeile um ein Handout zu erhalten.

Terminal
pdfjam --nup 2x2 --landscape --a4paper FILE.pdf --outfile HANDOUT.pdf

Ein Skript um alles zu builden

Jetzt haben wir alle einzelnen Schritte beisammen (3 Zeilen) um unsere Präsentation zu erstellen, eine .pdf Version und ein Handout. Das ist gut, allerdings wollen wir das natürlich nicht jedesmal händisch ausführen, sondern ein Skript anfertigen, das die Schritte automatisiert macht. Ich habe manchmal auch mehrere quarto Präsentationsfiles in einem Ordner liegen und möchte nicht jedes Mal darüber nachdenken welches ich nun rendern will. Das Skript sollte also folgendes können:

  1. render .qmd files – decktape – pdfjam
  2. für alle quarto files im Ordner
  3. parallel (da die Files z. T. längere Laufzeiten haben und mein Laptop > 10 Kerne hat) – es ist mir kurz gesagt egal ob er tw. umsonst rendert, ich will nur keine Zeit damit verschwenden über irgendwelche Files nachzudenken.

Das hier gezeigte Python Skript definiert eine Funktion process_file die im wesentlichen 3 Befehle ausführt: render – decktape – pdfjam. Die Dateinamen werden von den quarto Dokumenten übernommen und die Endungen angepasst. Um alles parallel ablaufen zu lassen wird hier der ThreadPoolExecutor verwendet.

Python
#| eval: FALSE
#| echo: true

from concurrent.futures import ThreadPoolExecutor
import os
import glob
import re
import sys

args = sys.argv[1:]

def process_file(val): # Function
    new_file_html = re.sub("qmd$", "html", val)
    new_file_pdf = re.sub("qmd$", "pdf", val)
    new_file_handout_pdf = re.sub(".qmd$", "_handout.pdf", val)
    
    run_quarto = "quarto render " + val
    run_pdf = f"docker run --rm -t -v {file_path}:/slides -v :{file_path} astefanutti/decktape --size='2048x1536' reveal {new_file_html} {new_file_pdf}"
    run_pdf_handout = f"pdfjam --nup 2x2 --landscape --a4paper {new_file_pdf} --outfile {new_file_handout_pdf}"
    
    os.system(run_quarto) # render
    os.system(run_pdf) # decktape
    os.system(run_pdf_handout) # pdfjam

if __name__ == "__main__":
    file_path = os.getcwd()
    quarto_files = "*.qmd"
    matching_files = glob.glob(os.path.join(file_path, quarto_files))
    file_names = [os.path.basename(pth) for pth in matching_files]

    with ThreadPoolExecutor(max_workers=4) as executor:
        executor.map(process_file, file_names)

if args[0] == "save":
    print("backup to google drive")
    os.system("bash -i -c backupLV2google")
1
Argumente werden eingesammelt. Ziel ist es einen Input zu ermöglichen in der Form: render_praes save bzw. render_praes. Wenn ‘save’ angegeben ist, dann soll von dem Folder auch ein backup gemacht werden (das wird jetzt nicht im Detail dargestellt – vielleicht in einem anderen Post mal).
2
Hier hat mir als N00b ChatGPT geholfen. Meine schön programmierte Schleife wurde zu diesem Konstrukt umformuliert.
3
Wenn ‘save’ angegeben wird, wird dieser Block getriggert. Den Teil kann man natürlich weglassen wenn man es nicht braucht.

Platzierung des Python Skripts

Um dieses Skript ohne copy/paste in unterschiedlichen Projekten verwenden zu können, will ich es zentral zur Verfügung stellen und zwar so, dass es immer wenn eine Linux Terminal geöffnet wird zur Verfügung steht. Wie machen wir das?

  1. Wechsle in das Verzeichnis in dem das Skript liegt cd da/is/mein/skript
  2. Mache das Python Skript ausführbar: chmod +x render_praes.py
  3. Kopiere/verschiebe das Skript an einen zentraleren Ort6 wie z. B. den ~/bin Ordner. Hat keinen derartigen Ordner, macht an zuerst mkdir ~/bin und dann cp render_praes.py ~/bin um das Skript reinzukopieren.
  4. Dann fügt man einen alias in die ~/.bashrc ein. Damit stellt man das Skript unter dem geannten Namen zur Verfügung – daher ist das der Moment, in dem man sich für einen Namen entscheiden muss. Hier ist der Name mit render_praes festgelegt.
~/.bashrc
alias render_praes='/home/manuel/bin/render_praes_parallel.py'

Um die Funktion auszuprobieren startet man ein neues Terminal, wechselt in den Ordner mit zumindest einer quarto revealjs Präsentation und schreibt render_preas.
Boom . Es sollte nun zuerst quarto rendern, decktape das .pdf erzeugen und pdfjam anschließend das Handout.
Und währenddessen kann man mal gemütlich einen Kaffee trinken gehen.

Footnotes

  1. Das ist eine wirklich gute kurze Einführung in Makefiles https://kbroman.org/minimal_make/↩︎

  2. Man lebt ja nicht ewig!↩︎

  3. Unter Linux muss man sich ein .deb file downloaden und das installieren. Ich würde RStudio empfehlen.↩︎

  4. STG + ALT + T öffnet ein Terminal in Linux.↩︎

  5. Man muss sich wirklich nicht mit Docker auskennen um Docker verwenden zu können!↩︎

  6. ist nicht unbedingt notwendig – aus Erfahrung kann ich aber sagen dass es sehr praktisch ist wenn man weiß wo solche Skripts liegen.↩︎

2025 Manuel Reif | site made with quarto