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 b50992682408210a9cd9b5965c6876ad5e704d7a..9201367e5373557fbd468e69403d77ccedf27edb 100644
--- a/aleksis/apps/chronos/templates/chronos/timetable.html
+++ b/aleksis/apps/chronos/templates/chronos/timetable.html
@@ -36,7 +36,7 @@
       {% endif %}
     </div>
     <div class="col s4 m6 l4 xl3 right align-right no-print">
-      <a class="waves-effect waves-teal btn-flat btn-flat-medium right" 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 77fed121b29a4e4cd7506776187a1f025f07224d..2bea6c244271a46c109ae7896e08c97e0e69df89 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)