diff --git a/README.rst b/README.rst
index c3fb19c3020e7b9458cfc3498abfe5284d1a1b8e..d96f30822fc6b5fb69216d81c8711f0299f0125d 100644
--- a/README.rst
+++ b/README.rst
@@ -31,7 +31,7 @@ Licence
   Copyright © 2019 Tom Teichler <tom.teichler@teckids.org>
   Copyright © 2019 Hangzhi Yu <yuha@katharineum.de>
 
-  Licenced under the EUPL, version 1.2 or later
+  Licenced under the EUPL, version 1.2 or later, by Teckids e.V. (Bonn, Germany).
 
 Please see the LICENCE.rst file accompanying this distribution for the
 full licence text or on the `European Union Public Licence`_ website
diff --git a/aleksis/apps/chronos/__init__.py b/aleksis/apps/chronos/__init__.py
index bed63d688b366a2a7a5ea587652167d345823993..482bbcdf18b41d99b02aa17beb45d5560060bee9 100644
--- a/aleksis/apps/chronos/__init__.py
+++ b/aleksis/apps/chronos/__init__.py
@@ -4,5 +4,3 @@ try:
     __version__ = pkg_resources.get_distribution("AlekSIS-App-Chronos").version
 except Exception:
     __version__ = "unknown"
-
-default_app_config = "aleksis.apps.chronos.apps.ChronosConfig"
diff --git a/aleksis/apps/chronos/migrations/0001_initial.py b/aleksis/apps/chronos/migrations/0001_initial.py
index 5d6f1b8f87925290f2319a8ac4805b7526504322..1b1c0ee95b474e02006d6d7c983bc3619176fca7 100644
--- a/aleksis/apps/chronos/migrations/0001_initial.py
+++ b/aleksis/apps/chronos/migrations/0001_initial.py
@@ -26,7 +26,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -35,7 +35,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -56,7 +56,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -65,7 +65,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -87,7 +87,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -96,7 +96,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -126,7 +126,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -135,7 +135,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -165,7 +165,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -174,7 +174,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -202,7 +202,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -211,7 +211,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -258,7 +258,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -267,7 +267,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -312,7 +312,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -321,7 +321,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -400,7 +400,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -409,7 +409,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -465,7 +465,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -474,7 +474,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -507,7 +507,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -516,7 +516,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -676,7 +676,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -685,7 +685,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -717,7 +717,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -726,7 +726,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -808,7 +808,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -817,7 +817,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -878,7 +878,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -887,7 +887,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -995,7 +995,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -1004,7 +1004,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
@@ -1039,7 +1039,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -1048,7 +1048,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
diff --git a/aleksis/apps/chronos/migrations/0002_school_term_validity.py b/aleksis/apps/chronos/migrations/0002_school_term_validity.py
index f890a222288307a8a7e93942fa43af2950724548..947ccad0a618cc00f2697d17c9d4f8e4d60b0d65 100644
--- a/aleksis/apps/chronos/migrations/0002_school_term_validity.py
+++ b/aleksis/apps/chronos/migrations/0002_school_term_validity.py
@@ -53,7 +53,7 @@ class Migration(migrations.Migration):
             fields=[
                 (
                     "id",
-                    models.AutoField(
+                    models.BigAutoField(
                         auto_created=True,
                         primary_key=True,
                         serialize=False,
@@ -62,7 +62,7 @@ class Migration(migrations.Migration):
                 ),
                 (
                     "extended_data",
-                    django.contrib.postgres.fields.jsonb.JSONField(
+                    models.JSONField(
                         default=dict, editable=False
                     ),
                 ),
diff --git a/aleksis/apps/chronos/static/css/chronos/timetable_print.css b/aleksis/apps/chronos/static/css/chronos/timetable_print.css
new file mode 100644
index 0000000000000000000000000000000000000000..688c009f6fd3c9a1dbee9378cab422d69defd730
--- /dev/null
+++ b/aleksis/apps/chronos/static/css/chronos/timetable_print.css
@@ -0,0 +1,32 @@
+.timetable-plan .row, .timetable-plan .col {
+    display: flex;
+    padding: 0rem;
+}
+
+.timetable-plan .row {
+    margin-bottom: 0rem;
+}
+
+.lesson-card, .timetable-title-card {
+    margin: 0;
+    display: flex;
+    flex-grow: 1;
+    min-height: 40px;
+    box-shadow: none;
+    border: 1px solid black;
+    margin-right: -1px;
+    margin-top: -1px;
+    border-radius: 0px;
+    font-size: 11px;
+}
+.lesson-card .card-content > div {
+    padding: 1px;
+}
+
+.card .card-title {
+    font-size: 18px;
+}
+
+.timetable-title-card .card-content {
+    padding: 7px;
+}
diff --git a/aleksis/apps/chronos/templates/chronos/timetable.html b/aleksis/apps/chronos/templates/chronos/timetable.html
index 07b8402d8c770ba225a16d6f563d8276f0827021..9201367e5373557fbd468e69403d77ccedf27edb 100644
--- a/aleksis/apps/chronos/templates/chronos/timetable.html
+++ b/aleksis/apps/chronos/templates/chronos/timetable.html
@@ -35,9 +35,8 @@
         </h5>
       {% endif %}
     </div>
-    {# Show print button only if not on mobile #}
     <div class="col s4 m6 l4 xl3 right align-right no-print">
-      <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" id="print">
+      <a class="waves-effect waves-teal btn-flat btn-flat-medium right hide-on-small-and-down" href="{% url "timetable_print" type.value pk %}" id="print">
         <i class="material-icons center">print</i>
       </a>
     </div>
diff --git a/aleksis/apps/chronos/templates/chronos/timetable_print.html b/aleksis/apps/chronos/templates/chronos/timetable_print.html
new file mode 100644
index 0000000000000000000000000000000000000000..4769673610019ad98538d11db579a9cbe6c4601c
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/timetable_print.html
@@ -0,0 +1,55 @@
+{% extends 'core/base_print.html' %}
+
+{% load data_helpers static i18n %}
+
+{% block extra_head %}
+  <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}">
+  <link rel="stylesheet" href="{% static 'css/chronos/timetable_print.css' %}">
+{% endblock %}
+
+{% block page_title %}
+  {% trans "Timetable" %} <i>{{ el.short_name }}</i>
+{% endblock %}
+{% block content %}
+  <div class="timetable-plan">
+    {#  Week days #}
+    <div class="row">
+      <div class="col s2">
+
+      </div>
+      {% for weekday in weekdays_short %}
+        <div class="col s2">
+          <div class="card timetable-title-card">
+            <div class="card-content">
+              <span class="card-title">
+                {{ weekday.name }}
+              </span>
+            </div>
+          </div>
+        </div>
+      {% endfor %}
+    </div>
+
+    {% for row in timetable %}
+      <div class="row">
+        <div class="col s2">
+          {% if row.type == "period" %}
+            {% include "chronos/partials/period_time.html" with period=row.period periods=periods %}
+          {% endif %}
+        </div>
+
+        {% for col in row.cols %}
+          {# A lesson #}
+          <div class="col s2">
+            {% if row.type == "period" %}
+              {% include "chronos/partials/elements.html" with elements=col %}
+            {% else %}
+              {% include "chronos/partials/supervision.html" with supervision=col %}
+            {% endif %}
+          </div>
+        {% endfor %}
+      </div>
+    {% endfor %}
+  </div>
+
+{% endblock %}
diff --git a/aleksis/apps/chronos/urls.py b/aleksis/apps/chronos/urls.py
index 666183ec6a43d80b897b05643288a77ee8b615b7..fc6d9a868e871b303a6dd0e1b6a8cdfc50be63ed 100644
--- a/aleksis/apps/chronos/urls.py
+++ b/aleksis/apps/chronos/urls.py
@@ -16,6 +16,12 @@ urlpatterns = [
         views.timetable,
         name="timetable_by_week",
     ),
+    path(
+        "timetable/<str:type_>/<int:pk>/print/",
+        views.timetable,
+        {"is_print": True},
+        name="timetable_print",
+    ),
     path(
         "timetable/<str:type_>/<int:pk>/<str:regular>/", views.timetable, name="timetable_regular",
     ),
diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py
index 7f929ca8bd9dac7b0da1c79b63c7751ca9a3649b..797eefcd95a18d9a3a930f24e8a7c50af9c92642 100644
--- a/aleksis/apps/chronos/util/chronos_helpers.py
+++ b/aleksis/apps/chronos/util/chronos_helpers.py
@@ -17,6 +17,8 @@ def get_el_by_pk(
     week: Optional[int] = None,
     regular: Optional[str] = None,
     prefetch: bool = False,
+    *args,
+    **kwargs,
 ):
     if type_ == TimetableType.GROUP.value:
         return get_object_or_404(
diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py
index b90f0b0ccf8ed0143197e49a9d50af62e467b9f8..50ebab5bf75762dd21f072c4a7282f0d9b4e3e0e 100644
--- a/aleksis/apps/chronos/views.py
+++ b/aleksis/apps/chronos/views.py
@@ -15,6 +15,7 @@ from rules.contrib.views import permission_required
 from aleksis.core.models import Announcement, Group, Person
 from aleksis.core.util import messages
 from aleksis.core.util.core_helpers import get_site_preferences, has_person
+from aleksis.core.util.pdf import render_pdf
 
 from .forms import LessonSubstitutionForm
 from .managers import TimetableType
@@ -124,12 +125,16 @@ def timetable(
     year: Optional[int] = None,
     week: Optional[int] = None,
     regular: Optional[str] = None,
+    is_print: bool = False,
 ) -> HttpResponse:
     """View a selected timetable for a person, group or room."""
     context = {}
 
     is_smart = regular != "regular"
 
+    if is_print:
+        is_smart = False
+
     el = get_el_by_pk(request, type_, pk, prefetch=True)
 
     if type(el) == HttpResponseNotFound:
@@ -185,7 +190,13 @@ def timetable(
         "timetable_by_week", args=[type_.value, pk, week_next.year, week_next.week]
     )
 
-    return render(request, "chronos/timetable.html", context)
+    if is_print:
+        context["back_url"] = reverse(
+            "timetable_by_week", args=[type_.value, pk, wanted_week.year, wanted_week.week],
+        )
+        return render_pdf(request, "chronos/timetable_print.html", context)
+    else:
+        return render(request, "chronos/timetable.html", context)
 
 
 @permission_required("chronos.view_lessons_day")
@@ -346,9 +357,8 @@ def substitutions(
             wanted_day, "substitutions_by_date"
         )
 
-        template_name = "chronos/substitutions.html"
+        return render(request, "chronos/substitutions.html", context)
     else:
         context["days"] = day_contexts
-        template_name = "chronos/substitutions_print.html"
 
-    return render(request, template_name, context)
+        return render_pdf(request, "chronos/substitutions_print.html", context)