Wie nutze ich Emacs zum Blogging
Ich hatte von Anfang an für meinen Blog die Einträge in Emacs geschrieben. Dazu verwende ich den orgmode. Nenn mich altmodisch, verkalkt oder sonst wie. Ich mag Emacs und orgmode. Bei mir hat es irgendwie Klick gemacht und ich fühle mich mit dieser 50 Jahre alten Software sehr wohl. Für diesen Blog gibt es jetzt eine einzelne Org-Datei. Der gesamte Blog ist darin enthalten. Jeder Beitrag ist ein TODO. Um daraus dann eine Webseite zu erstellen ist der Ablauf wie folgt:
- In der Org-Datei einen Beitrag schreiben
- Properties für Hugo im Beitrag ergänzen
- Mittels ox-Hugo einen Export Richtung Hugo durchführen
- Das Ergebnis lokal mit Hugo prüfen
- Aus Emacs heraus per Lisp-Funktion einen Export in Hugo durchführen und das Ergebnis auf den Webserver hochladen
In diesem Artikel werde ich versuchen zu beschreiben, was dafür eingerichtet werden muss. Mit Sicherheit vergesse ich das ein oder andere. Aber die Dokumentation von ox-hugo und hugo sind sehr umfangreich.
Einrichtung
ox-hugo in Emacs einrichten
Viel Einrichtung wird nicht benötigt. ox-hugo kann über use-package einfach in Emacs integriert werden. Ich habe einfach den entsprechenden Eintrag aus der Installationsanleitung übernommen. Da ich für Emacs eine org-Datei als Konfiguration pflege, musste ich die Datei mittels org-babel-tangle noch als init.el exportieren. Danach Emacs neustarten und ox-hugo steht bereit.
(use-package ox-hugo
:ensure t
:pin melpa
:after ox)
hugo vorbereiten
Installation
hugo sollte in jeder Linux-Distribution vorhanden sein. Unter openSUSE Slowroll habe ich einfach mittels zypper in hugo das Paket installiert.
Ordner einrichten
Ich habe in meinem Home-Verzeichnis mittels hugo direkt eine neue Seite angelegt. In dem Quick Start-Guide von hugo wird das beschrieben. Ich habe bei mir das Verzeichnis auf blog geändert.
hugo new site blog
hugo legt den Ordner blog an. Darin erzeugt hugo die notwendige Verzeichnisstruktur und die Konfigurationsdatei hugo.toml.
Theme
Ich habe PaperMod als Theme verwendet. Mir gefiel das Thema auf Anhieb. Es wird im Quick Start-Guide von Hugo auch aufgeführt. Da ich den Ordner über meine NAS mit zwei Computern synchronisiere, habe ich mir das ZIP-Archiv des Theme heruntergeladen. Das Theme muss in dem Ordner themes/papermod entpackt werden. So stelle ich sicher, dass das Theme auf beiden Computern identisch ist. Es ist dann irrelevant, wo ich einen Eintrag schreibe, ich kann immer sicher einen Export und Upload durchführen.
Footer
Ich habe den Footer erweitert. Zum einen habe ich dort das Impressum und die Datenschutzerklärung integriert. Zum anderen habe ich dort auch Emacs und ox-hugo aufgeführt und verlinkt. Ein wenig Fanboy muss sein.
Damit hugo solche Änderungen am Theme übernimmt, kann man im Ordner layouts/partials eigene Dateien anlegen, die dann das Theme übersteuern. Ich habe dort die Datei footer.html aus dem papermod-theme kopiert und angepasst. Bei mir sieht die Erweiterung so aus:
<span>
Powered by
<a href="https://gohugo.io/?utm_source=papermod" rel="noopener" target="_blank">Hugo</a> &
<a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>
</span>
<span> · </span>
<span>
Written in
<a href="https://www.gnu.org/software/emacs/" rel="noopener noreferrer" target="_blank">Emacs</a> &
<a href="https://ox-hugo.scripter.co/" rel="noopener noreferrer" target="_blank">ox-hugo</a>
</span>
<span> · </span>
<span>
<a href="/impressum/">Impressum</a> ·
<a href="/privacy/">Datenschutz</a>
</span>
CSS
Hier habe ich mir von KI helfen lassen. Ich brauchte zwei Änderungen. Zum einen wollte ich unbedingt eine andere Schriftart verwenden. Zum anderen brauchte ich für einen Beitrag eine Art “Spoiler”-Block. Wahrscheinlich kann man die CSS-Anpassung besser machen. Es funktioniert aber so und ist für mich ausreichend.
Anpassungen mittels CSS werden im Ordner assets/css/extended/ in der Datei custom.css abgelegt.
@font-face {
font-family: 'Aporetic';
src: url('/fonts/aporetic-serif-normalregularupright.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
/* Für Fettgedrucktes */
@font-face {
font-family: 'Aporetic';
src: url('/fonts/aporetic-serif-normalboldupright.woff2') format('woff2');
font-weight: bold;
font-style: normal;
}
/* Für Kursives */
@font-face {
font-family: 'Aporetic';
src: url('/fonts/aporetic-serif-normalregularitalic.woff2') format('woff2');
font-weight: normal;
font-style: italic;
}
/* Für Kursives und Fettgedrucktes */
@font-face {
font-family: 'Aporetic';
src: url('/fonts/aporetic-serif-normalbolditalic.woff2') format('woff2');
font-weight: bold;
font-style: italic;
}
.main {
max-width: 1000px;
margin-left: auto;
margin-right: auto;
padding: 0 20px;
}
/* Der Spoiler-Container */
.spoiler-block {
margin: 20px 0;
padding: 10px;
border: 1px solid var(--border);
border-radius: 8px;
background: var(--code-bg);
}
/* Der Klick-Text */
.spoiler-block summary {
cursor: pointer;
font-weight: bold;
color: var(--primary);
list-style: none; /* Entfernt den Standard-Pfeil in einigen Browsern */
}
.spoiler-block summary::-webkit-details-marker {
display: none; /* Entfernt den Pfeil in Chrome/Safari */
}
/* Ein kleiner Hinweis vor dem Text */
.spoiler-block summary::before {
font-family: 'Aporetic';
src: url('/fonts/aporetic-serif-normalregularupright.woff2') format('woff2');
font-weight: bold;
content: "👁️ ";
margin-right: 10px;
}
/* Der Inhalt (das Bild) */
.spoiler-content {
font-family: 'Aporetic';
src: url('/fonts/aporetic-serif-normalregularupright.woff2') format('woff2');
margin-top: 15px;
border-top: 1px dashed var(--border);
padding-top: 15px;
}
body {
font-family: 'Aporetic', serif;
}
.post-content {
font-family: 'Aporetic', serif;
line-height: 1.6;
font-size: 1.15rem;
}
.post-content img {
border-radius: 8px;
margin: 20px 0;
}
Konfiguration
Um Einstellungen für den Blog vorzunehmen, wird die Datei hugo.toml im Hauptverzeichnis genutzt. Ich habe einige Anpassungen vorgenommen, um z. B. den Blog auf deutsch umzustellen.
baseURL = 'https://jan-iversen.de/'
locale = 'de-de'
title = 'Blog '
languageCode = 'de-de'
defaultContentLanguage = 'de'
enableRobotsTXT = true
[module]
[[module.imports]]
path = "github.com/adityatelange/hugo-PaperMod"
[markup.goldmark.renderer]
unsafe = true
[outputs]
home = ["HTML", "RSS", "JSON"]
[taxonomies]
tag = "tags"
category = "categories"
[params]
ShowToc = true
TocOpen = false
ShowReadingTime = true
ShowWordCount = true
ShowPostNavLinks = true
defaultTheme = "auto"
showThemeToggle = true
ShowFullTextinRSS = true
[languages]
[languages.de]
languageName = "Deutsch"
weight = 1
title = "Jan Iversen"
Schriftart Aporetic vorbereiten
Ich möchte die Schriftart Aporetic in diesem Blog verwenden. Da die TTF-Dateien sehr groß sind, habe ich sie in WOFF2-Dateien umgewandelt. Zusätzlich habe ich die Anzahl der Zeichen drastisch reduziert. Die Schriftart enthält nur noch die lateinischen sowie einige Sonderzeichen. Dadurch ist ein einzelnes Font nur noch ca. 30 KByte groß. Ich halte das für einen guten Kompromiss. So hat die Seite eine persönliche Note und zugleich zwinge ich niemanden größere Datenmengen laden zu müssen, um die Seite mit der Schrift darzustellen.
Unter Tumbleweed können die Schriftarte wie folgt umgewandelt werden:
for f in *.ttf; do
pyftsubset "$f" \
--unicodes="U+0000-00FF,U+0150-0151,U+02C6,U+02DA,U+02DC,U+2013-2014,U+2018-201B,U+20AC,U+2122" \
--flavor=woff2 \
--output-file="${f%.ttf}.woff2"
done
Die Schriftarten werden im Blog-Verzeichnis unter static/fonts abgelegt. hugo übernimmt die Dateien dann beim Export von dort.
Webserver - Nginx
Ich habe Nginx auf einem Webserver bereits im Einsatz gehabt. Ich gehe hier nicht detailliert auf die Konfiguration ein. Da micro.blog Beiträge in der URL mittels post/jahr/monat/beitrag abgelegt und in diesem Blog die Beiträge direkt unter posts/beitragstitel aufgerufen werden habe ich einen rewrite konfiguriert. Dadurch werden die Beiträge gefunden, wenn jemand einen alten Link verwendet.
Emacs - Funktion zum Hochladen
Hier habe ich die KI einmal bemüht. Die folgende Lisp-Funktion führt den Export in hugo aus und lädt anschließend die Dateien mittels rsync auf den Webserver.
(defun jan/blog-deploy ()
"Exportiert den Blog mit Hugo und lädt ihn per rsync auf den V-Server hoch."
(interactive)
(let ((default-directory "~/blog/"))
(message "Starte Hugo Build...")
(if (zerop (shell-command "hugo --gc --minify"))
(progn
(message "Upload zum Server läuft...")
(shell-command "rsync -avz --delete public/ benutzer@servername:/var/www/jan-iversen.de/public/")
(message "Blog erfolgreich veröffentlicht!"))
(error "Fehler beim Hugo-Build! Upload abgebrochen."))))
Eintrag schreiben…
Nun der spannende Teil. Wie sieht die Org-Datei aus?
Es gibt drei Bereiche. Die Eigenschaften der Datei. Hier sind die Basis-Informationen für ox-hugo bzw. Hugo enthalten. Dann gibt es den Bereich “Statische Seiten”. Darin sind die Seiten wie “über mich”, “Kontakt” oder auch das “Impressum” enthalten. Und dann kommt der Bereich “Blog-Beiträge”. Hier schreibe ich gerade diesen Beitrag. Ein Beitrag ist immer ein TODO. Erst wenn der Status DONE erreicht ist, wird ein Eintrag überhaupt exportiert. Ich kann also in dieser Datei grundsätzlich an mehreren Beiträgen schreiben. Wenn ich dann fertig bin, setze ich den Status auf Done. Ansonsten ist das Schreiben wie in jeder anderen Org-Datei auch. Ich kann Formatierungen verwenden, Überschriften setzen, Source-Code-Blöcke verwenden, usw. Eben alles, was der orgmode kann. ox-hugo konvertiert dann alles in Markdown beim Export.
… und veröffentlichen
Ich bin jetzt fertig mit dem Beitrag. Und es gibt nur zwei Punkte, die ich jetzt machen muss. Ich rufe M-x org-export-dispatch auf. Und wähle dort einmal H-H. ox-hugo exportiert jetzt den Beitrag. Möchte ich alles exportieren, dann ist H-A der richtige Export.
Danach kann ich mir lokal das Ergebnis anschauen. Dazu einmal die Konsole starten und in das Blogverzeichnis wechseln. Dort mit hugo server -D -disableFastRender hugo starten. In der Konsole steht eine URL für localhost. Damit kann im Browser das Ergebnis geprüft werden.
Ist alles ok, rufe ich nur über M-x meine Funktion jan/blog-deploy aus. Und alles wird auf den Server geladen.
Fazit
Ich bin mit dem Setup sehr zufrieden. Warum ich gewechselt bin, habe ich “Warum habe ich den Umzug durchgeführt?” bereits beschrieben. Vielleicht hilft jemanden diese Anleitung. Für mich ist das quasi noch einmal eine Dokumentation für mich. Und ich konnte für mich auch noch einmal den Ablauf retrospektiv betrachten und Lücken in meiner eigenen Dokumentation finden.
Emacs in Aktion

Abbildung 1: Emacs in Aktion
Links
Für weitere Informationen empfehle ich das Lesen der Dokumentationen: