diff --git a/Dockerfile b/Dockerfile
index c65b91b9622fc4a3d2f3933d111a0ee4c632134a..1b1f32be0891da9a0ccbd967cd07f382caae5eb8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,6 +3,8 @@ FROM python:3.8
 RUN apt-get update && apt-get install -y \
     # requirements for OpenCV
     libgl1-mesa-dev \
+    # requirements for WeasyPrint
+    python3-cffi \
     && rm -rf /var/lib/apt/lists/*
 
 RUN mkdir /app
diff --git a/elections2021/management/commands/export_program.py b/elections2021/management/commands/export_program.py
index 8e8c82ac5d25432f13c7ea4d9c70dabe9fa449ef..0d16f0d70754e6b82f3b9ae07cd234bc3575d5ff 100644
--- a/elections2021/management/commands/export_program.py
+++ b/elections2021/management/commands/export_program.py
@@ -19,53 +19,32 @@ class Command(BaseCommand):
         parser.add_argument("output", type=str, help=".pdf nebo .html soubor")
 
     def handle(self, *args, **options):
-        now = timezone.localtime().strftime("%d.%m.%Y %H:%M")
-
         toc = []
         body = []
 
         for ministry, title in MINISTRY_CHOICES:
             sub_toc = []
-
-            body.append(
-                f'<h2 id="{ministry}" class="ministry-title">Resort: {title}</h2>'
-            )
+            points = []
             for page in get_ministry_points(ministry):
                 value = render_to_string(
                     "elections2021/export_program_point.html", {"page": page}
                 )
                 value = re.sub(r'href="#zdroje"', f'href="#zdroje_{page.id}"', value)
-                body.append(value)
-
-                sub_toc.append(f'<li><a href="#{page.slug}">{page.title}</a></li>')
-
-            sub_toc = "".join(sub_toc)
-            toc.append(
-                f'<li><a href="#{ministry}">{title}</a><br><ul>{sub_toc}</ul></li>'
-            )
-
-        style = """
-            body { font-family: "Roboto Condensed", Helvetica, sans-serif; line-height: 1.5; }
-            .ministry-title { page-break-before: always; margin-bottom: 0; line-height: 1; }
-            .point-title { border-top: 5px double #AAAAAA; padding-top: 0.5rem; margin-top: 3rem; text-transform: uppercase; }
-            .section-title { border-top: 1px solid #AAAAAA; padding-top: 0.5rem; margin-top: 2rem; text-transform: uppercase; }
-            sup { font-size: 70%; }
-        """
-
-        toc = "".join(toc)
-        content = (
-            [
-                f'<html><head><style type="text/css">{style}</style></head><body>',
-                "<h1>Program koalice Piráti a Starostové</h1>",
-                f'<div><p>export z webu <a href="https://www.piratiastarostove.cz/">piratiastarostove.cz</a> z {now}</p></div>',
-                f"<div><p>obsah:</p><p><ul>{toc}</ul></p></div>",
-            ]
-            + body
-            + ["</body></html>"]
+                points.append(value)
+                sub_toc.append({"anchor": page.slug, "title": page.title})
+
+            body.append({"anchor": ministry, "title": title, "points": points})
+            toc.append({"anchor": ministry, "title": title, "sub_toc": sub_toc})
+
+        content = render_to_string(
+            "elections2021/export_program.html",
+            {
+                "toc": toc,
+                "body": body,
+                "now": timezone.localtime().strftime("%d.%m.%Y %H:%M"),
+            },
         )
 
-        content = "".join(content)
-
         if options["output"].endswith(".pdf"):
             html = HTML(string=content)
             css = CSS(string="@page { size: A4; margin: 1cm; }")
diff --git a/elections2021/templates/elections2021/export_program.html b/elections2021/templates/elections2021/export_program.html
new file mode 100644
index 0000000000000000000000000000000000000000..0db3db2c670d8bb50e750db041fd6844c3b8b946
--- /dev/null
+++ b/elections2021/templates/elections2021/export_program.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html lang="cs">
+<head>
+  <meta charset="utf-8">
+  <title>Program koalice Piráti a Starostové</title>
+  <style type="text/css">
+    body {
+      font-family: "Roboto Condensed", Helvetica, sans-serif;
+      line-height: 1.5;
+    }
+    .ministry-title {
+      page-break-before: always;
+      margin-bottom: 0;
+      line-height: 1;
+      color: #FFFFFF;
+      background-color: #000000;
+      padding: 0.5rem;
+    }
+    .point-title {
+      border-top: 5px double #AAAAAA;
+      padding-top: 0.5rem;
+      margin-top: 2rem;
+      text-transform: uppercase;
+    }
+    .section-title {
+      border-top: 1px solid #AAAAAA;
+      padding-top: 0.5rem;
+      margin-top: 2rem;
+      text-transform: uppercase;
+    }
+    sup {
+      font-size: 70%;
+    }
+    .last {
+      margin-bottom: 3rem;
+    }
+  </style>
+</head>
+<body>
+
+<h1>Program koalice Piráti a Starostové</h1>
+<div>
+  <p>export z webu <a href="https://www.piratiastarostove.cz/">piratiastarostove.cz</a> z {{ now }}</p>
+  <p>obsah:</p>
+  <ul>
+    {% for item in toc %}
+      <li>
+        <a href="#{{ item.anchor }}">{{ item.title }}</a><br>
+        <ul>
+          {% for sub in item.sub_toc %}
+            <li><a href="#{{ sub.anchor }}">{{ sub.title }}</a></li>
+          {% endfor %}
+        </ul>
+      </li>
+    {% endfor %}
+  </ul>
+</div>
+
+{% for item in body %}
+  <h2 id="{{ item.anchor }}" class="ministry-title">Resort: {{ item.title }}</h2>
+  {% for point in item.points %}
+    {{ point|safe }}
+  {% endfor %}
+{% endfor %}
+
+</body>
+</html>
diff --git a/elections2021/templates/elections2021/export_program_point.html b/elections2021/templates/elections2021/export_program_point.html
index df3f2c567f84db8dc7fcfceb8a24a9e76dd2211a..a04925f09df6afe01bd0a2bff83eb60205961299 100644
--- a/elections2021/templates/elections2021/export_program_point.html
+++ b/elections2021/templates/elections2021/export_program_point.html
@@ -53,4 +53,4 @@
 </div>
 
 <h3 class="section-title" id="zdroje_{{ page.id }}">Klidně si to ověřte</h3>
-<div>{{ page.sources|richtext }}</div>
+<div class="last">{{ page.sources|richtext }}</div>