diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..43a28b4a0487cc62017210c9d2b81fbdf5e98b00
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/print
diff --git a/Kare Raisu.md b/Kare_Raisu.md
similarity index 100%
rename from Kare Raisu.md
rename to Kare_Raisu.md
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b4d15ba8c150e9dda008a6400b2648dadc0cf5e2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
+SOURCES := $(wildcard *.md)
+OBJECTS := $(addprefix print/,$(SOURCES:%.md=%.pdf))
+
+.PHONY: all
+all: $(OBJECTS)
+
+print/%.pdf: %.md meta/header.html meta/wrap_sections.mjs
+	mkdir -p "$(@D)"
+	pandoc "$<" -t html -H meta/header.html --filter meta/wrap_sections.mjs -o - | wkhtmltopdf -B 10mm -L 20mm -R 10mm -T 10mm --javascript-delay 0 - "$@"
diff --git a/Miso Ramen.md b/Miso_Ramen.md
similarity index 100%
rename from Miso Ramen.md
rename to Miso_Ramen.md
diff --git a/MuzenBerliner.md b/Muzen_Berliner.md
similarity index 100%
rename from MuzenBerliner.md
rename to Muzen_Berliner.md
diff --git a/meta/header.html b/meta/header.html
new file mode 100644
index 0000000000000000000000000000000000000000..211f1523ec4bbc7b1bf5838bc407945630fa8be7
--- /dev/null
+++ b/meta/header.html
@@ -0,0 +1,36 @@
+<meta charset="utf-8">
+<style>
+h1,h2,h3,h4,h5,h6 {
+  font-family: "Franziska Pro";
+  font-weight: 400;
+}
+
+th {
+  display: none;
+}
+
+:root {
+  font-family: "Roboto Condensed";
+  font-weight: 200;
+  line-height: 1.5;
+  font-size: 14pt;
+}
+
+p, li {
+  margin-bottom: 0.75rem;
+}
+
+td {
+  font-weight: 200;
+  font-size: 1rem;
+}
+
+body > div > div {
+  page-break-inside: avoid;
+}
+
+div#zutaten table td:first-child {
+  padding-right: 1rem;
+  min-width: 6rem;
+}
+</style>
diff --git a/meta/wrap_sections.mjs b/meta/wrap_sections.mjs
new file mode 100755
index 0000000000000000000000000000000000000000..ec76a53f7a813785d68845621487e800be14ee0b
--- /dev/null
+++ b/meta/wrap_sections.mjs
@@ -0,0 +1,53 @@
+#!/usr/bin/env node
+import readline from "readline";
+
+function groupChildren(blocks, parentLevel) {
+  const result = [];
+  while (blocks.length) {
+    const block = blocks.shift();
+    if (block.t == "Header") {
+      const level = block.c[0];
+      const id = block.c[1][0];
+
+      if (parentLevel >= level) {
+        blocks.unshift(block);
+        return result;
+      } else {
+        const children = groupChildren(blocks, level);
+        result.push({
+          "t": "RawBlock",
+          "c": [
+            "html",
+            "<div id=\"" + id + "\">"
+          ]
+        });
+        result.push(block);
+        result.push(...children);
+        result.push({
+          "t": "RawBlock",
+          "c": [
+            "html",
+            "</div>"
+          ]
+        });
+      }
+    } else {
+      result.push(block);
+    }
+  }
+  return result;
+}
+
+const rl = readline.createInterface({
+  input: process.stdin,
+  output: process.stdout,
+  terminal: false
+});
+rl.on('line', function (line) {
+  const data = JSON.parse(line);
+  const blocks = groupChildren(data.blocks, 0);
+  console.log(JSON.stringify({
+    ...data,
+    blocks: blocks
+  }));
+});