diff --git a/aleksis/apps/chronos/settings.py b/aleksis/apps/chronos/settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ae08d71d21171078752c8f2d80b719b2668e4cc
--- /dev/null
+++ b/aleksis/apps/chronos/settings.py
@@ -0,0 +1,8 @@
+from django.utils.translation import gettext_lazy as _
+
+CONSTANCE_CONFIG = {
+    "CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER": (2, _("Number of days shown on substitutions print view")),
+}
+CONSTANCE_CONFIG_FIELDSETS = {
+    "Chronos settings": ("CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER",),
+}
diff --git a/aleksis/apps/chronos/static/css/chronos/timetable.css b/aleksis/apps/chronos/static/css/chronos/timetable.css
index c10a7c1942a4e7585d3700f97bc73046ca1b9df1..324272585c7a0447c8f2e901fafbfb7be94dfa7a 100644
--- a/aleksis/apps/chronos/static/css/chronos/timetable.css
+++ b/aleksis/apps/chronos/static/css/chronos/timetable.css
@@ -96,3 +96,15 @@ table.substitutions td, table.substitutions th {
     margin: 1%;
     width: 30%;
 }
+
+.print-body table.substitutions td, .print-body table.substitutions th {
+    padding: 0 2px;
+}
+
+.print-body span.badge.new {
+    font-size: 0.9rem;
+    line-height: 20px;
+    height: 20px;
+    margin: 2px;
+    letter-spacing: 0.3pt;
+}
diff --git a/aleksis/apps/chronos/templates/chronos/substitutions.html b/aleksis/apps/chronos/templates/chronos/substitutions.html
index 2d0c99044c421e22d9bea5a3ac66733ee471c7fa..32e98c56d705fc815f709a7cbcc063c5206cb191 100644
--- a/aleksis/apps/chronos/templates/chronos/substitutions.html
+++ b/aleksis/apps/chronos/templates/chronos/substitutions.html
@@ -17,16 +17,10 @@
       <h4>{% trans "Substitutions" %}</h4>
     </div>
     <div class="col s2 m6 right align-right print-icon">
-      {#            <a class="waves-effect waves-teal btn-flat btn-flat-medium right"#}
-      {#               href="#}
-      {#                    {% if debug %}#}
-      {#                        {% url "timetable_substitutions_pdf_date" date|date:"Y-m-d" %}#}
-      {#                    {% else %}#}
-      {#                       {% url "timetable_substitutions_pdf" %}#}
-      {#                    {% endif %}#}
-      {#                    ">#}
-      {#                <i class="material-icons center">print</i>#}
-      {#            </a>#}
+      <a class="waves-effect waves-teal btn-flat btn-flat-medium right"
+         href="{% url "substitutions_print_by_date" day.year day.month day.day %}" target="_blank">
+        <i class="material-icons center">print</i>
+      </a>
     </div>
   </div>
 
@@ -79,7 +73,8 @@
       </td>
     {% endif %}
     {% for sub in substitutions %}
-      <tr class="{% if sub.type_ == "cancellation" %}green-text{% else %}black-text{% endif %}"> {# TODO: Extend support for blue and purple (supervisions and events) #}
+      <tr class="{% if sub.type_ == "cancellation" %}green-text{% else %}black-text{% endif %}">
+        {# TODO: Extend support for blue and purple (supervisions and events) #}
         <td>
           {% include "chronos/partials/groups.html" with groups=sub.lesson_period.lesson.groups.all %}
         </td>
@@ -102,7 +97,7 @@
             {# TODO: Support other cases#}
             <span class="badge new green hide-on-med-and-up">{% trans "Cancelled" %}</span>
           {% endif %}
-{#          <em>{{ sub.text|default:"" }}</em>#}
+          {#          <em>{{ sub.text|default:"" }}</em>#}
         </td>
         <td class="hide-on-small-and-down">
           {% if sub.cancelled %}
diff --git a/aleksis/apps/chronos/templates/chronos/substitutions_print.html b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
index 9f30bd141cb846f71b88e5fb3cb6a9e766ecf271..2a4e474bb2fc407f1297efb754d984140fc30fd0 100644
--- a/aleksis/apps/chronos/templates/chronos/substitutions_print.html
+++ b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
@@ -1,111 +1,98 @@
 {# -*- engine:django -*- #}
 
-{% extends 'core/base.html' %}
-{% load common %}
+{% extends 'core/base_print.html' %}
 
-{% block content %}
-<script type="text/javascript">
-    var dest = Urls.substitutions();
-</script>
+{% load i18n static %}
 
-<style>
-    table.substitutions td, table.substitutions th {
-        padding: 0 2px;
-    }
+{% block extra_head %}
+  <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}">
+{% endblock %}
 
-    span.badge.new {
-        font-size: 0.9rem;
-        line-height: 20px;
-        height: 20px;
-        margin: 2px;
-        letter-spacing: 0.3pt;
-    }
-</style>
+{% block browser_title %}{% blocktrans %}Print: Substitutions{% endblocktrans %}{% endblock %}
+{% block page_title %}{% blocktrans %}Substitutions{% endblocktrans %}{% endblock %}
 
-{% for c in days %}
-    <h4>Substitutions {{ c.date|date" }}</h4>
 
+{% block content %}
+  {% for day, c in days.items %}
+    <h4>{% trans "Substitutions" %} {{ c.day|date:"l" }} {{ c.day }}</h4>
 
-    {% include "timetable/hintsinsubprint.html" %}
+    {#    {% include "timetable/hintsinsubprint.html" %}#}
 
-    <div style="margin-bottom: 20px">
-        {% if c.header_info.is_box_needed %}
-            {% for row in c.header_info.rows %}
-                <div class="row no-margin">
-                    <div class="col s3 no-padding">
-                        <strong>{{ row.0 }}</strong>
-                    </div>
-                    <div class="col s9 no-padding">
-                        {{ row.1 }}
-                    </div>
-                </div>
-            {% endfor %}
-        {% endif %}
-    </div>
+    {#    <div style="margin-bottom: 20px">#}
+    {#        {% if c.header_info.is_box_needed %}#}
+    {#            {% for row in c.header_info.rows %}#}
+    {#                <div class="row no-margin">#}
+    {#                    <div class="col s3 no-padding">#}
+    {#                        <strong>{{ row.0 }}</strong>#}
+    {#                    </div>#}
+    {#                    <div class="col s9 no-padding">#}
+    {#                        {{ row.1 }}#}
+    {#                    </div>#}
+    {#                </div>#}
+    {#            {% endfor %}#}
+    {#        {% endif %}#}
+    {#    </div>#}
 
     <table class="substitutions">
-        <thead>
-        <tr>
-            <th><i class="material-icons">people</i></th>
-            <th><i class="material-icons">access_time</i></th>
-            <th>{% blocktrans %}Teachers{% endblocktrans %}</th>
-            <th>{% blocktrans %}Subject{% endblocktrans %}</th>
-            <th>{% blocktrans %}Room{% endblocktrans %}</th>
-            <th>{% blocktrans %}Hint{% endblocktrans %}</th>
-            <th></th>
-        </tr>
-        </thead>
-        <tbody>
-        {% if not c.sub_table %}
-            <td colspan="7">
-                <p class="flow-text center">
-                    {% blocktrans %}No existing substitutions{% endblocktrans %}
-                </p>
-            </td>
-        {% endif %}
-
-        {% set color_background = 1 %}
-        {% set last_classes = "" %}
+      <thead>
+      <tr>
+        <th><i class="material-icons">people</i></th>
+        <th><i class="material-icons">access_time</i></th>
+        <th>{% blocktrans %}Teachers{% endblocktrans %}</th>
+        <th>{% blocktrans %}Subject{% endblocktrans %}</th>
+        <th>{% blocktrans %}Room{% endblocktrans %}</th>
+        <th>{% blocktrans %}Notes{% endblocktrans %}</th>
+        <th></th>
+      </tr>
+      </thead>
 
-        {% for sub in c.sub_table %}
+      {% if not c.substitutions %}
+        <tbody>
+        <tr class="striped">
+          <td colspan="7">
+            <p class="flow-text center">
+              {% blocktrans %}No substitutions available.{% endblocktrans %}
+            </p>
+          </td>
+        </tr>
+        </tbody>
+      {% endif %}
 
-            {#  Color groups of classes in grey/white #}
-            {% if last_classes != sub.classes %}
-                {% if color_background %}{% set color_background = 0 %}
-                    {% else %}{% set color_background = 1 %}
-                {% endif %}
+      <tbody>
+      {% for sub in c.substitutions %}
+        <tr class="{% if sub.type_ == "cancellation" %}green-text{% else %}black-text{% endif %} ">
+          <td>
+            {% include "chronos/partials/groups.html" with groups=sub.lesson_period.lesson.groups.all %}
+          </td>
+          <td>
+            <strong>
+              {{ sub.lesson_period.period.period }}.
+            </strong>
+          </td>
+          <td>
+            {% include "chronos/partials/subs/teachers.html" %}
+          </td>
+          <td>
+            {% include "chronos/partials/subs/subject.html" %}
+          </td>
+          <td>
+            {% include "chronos/partials/subs/room.html" %}
+          </td>
+          <td>
+            {% if sub.cancelled %}
+              {# TODO: Support other cases#}
+              <span class="badge new green">{% trans "Cancelled" %}</span>
             {% endif %}
-            {% set last_classes = sub.classes %}
-
+            {#          <em>{{ sub.text|default:"" }}</em>#}
+          </td>
+        </tr>
 
-            <tr class="{{ sub.color }}-text {% if color_background %}striped{% endif %}">
-                <td>
-                    {{ sub.classes }}
-                </td>
-                <td>
-                    <strong>
-                        {{ sub.lesson }}
-                    </strong>
-                </td>
-                <td>
-                    {% include "timetable/subs/teacher.html" %}
-                </td>
-                <td>
-                    {% include "timetable/subs/subject.html" %}
-                </td>
-                <td>
-                    {% include "timetable/subs/room.html" %}
-                </td>
-                <td>
-                    {% if sub.badge %}
-                        <span class="badge new green">{{ sub.badge }}</span>
-                    {% endif %}
-                    <em>{{ sub.text|default:"" }}</em>
-                </td>
-            </tr>
-        {% endfor %}
-        </tbody>
+        {% ifchanged sub.lesson_period.lesson.groups %}
+          </tbody>
+          <tbody class="{% cycle "not-striped" "striped" %}">
+        {% endifchanged %}
+      {% endfor %}
+      </tbody>
     </table>
-
-{% endfor %}
+  {% endfor %}
 {% endblock %}
diff --git a/aleksis/apps/chronos/urls.py b/aleksis/apps/chronos/urls.py
index fb3e7c2e0fe25027493b3d5fcf6c2816846c68a3..d5311f492f6592dfb66f4fa5b03b48fc76126188 100644
--- a/aleksis/apps/chronos/urls.py
+++ b/aleksis/apps/chronos/urls.py
@@ -22,5 +22,7 @@ urlpatterns = [
         name="delete_substitution",
     ),
     path("substitutions/", views.substitutions, name="substitutions"),
+    path("substitutions/print/", views.substitutions, {"is_print": True}, name="substitutions_print"),
     path("substitutions/<int:year>/<int:month>/<int:day>/", views.substitutions, name="substitutions_by_date"),
+    path("substitutions/<int:year>/<int:month>/<int:day>/print/", views.substitutions, {"is_print": True}, name="substitutions_print_by_date"),
 ]
diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py
index 66ddde4a514ac2019b55dd4855b7886554a22356..388307c0a01ba3bcde067d456ea41b6348ed1223 100644
--- a/aleksis/apps/chronos/views.py
+++ b/aleksis/apps/chronos/views.py
@@ -2,6 +2,7 @@ from collections import OrderedDict
 from datetime import date, datetime, timedelta
 from typing import Optional, Tuple
 
+from constance import config
 from django.contrib.auth.decorators import login_required
 from django.db.models import Count
 from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
@@ -298,6 +299,7 @@ def substitutions(
     year: Optional[int] = None,
     month: Optional[int] = None,
     day: Optional[int] = None,
+    is_print: bool = False,
 ) -> HttpResponse:
     context = {}
 
@@ -307,17 +309,36 @@ def substitutions(
     else:
         wanted_day = get_next_relevant_day(timezone.now().date(), datetime.now().time())
 
-    substitutions = LessonSubstitution.objects.on_day(wanted_day)
+    day_number = config.CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER
+    day_contexts = {}
 
-    context["substitutions"] = substitutions
-    context["day"] = wanted_day
-    context["datepicker"] = {
-        "date": date_unix(wanted_day),
-        "dest": reverse("substitutions")
-    }
+    if is_print:
+        next_day = wanted_day
+        for i in range(day_number):
+            day_contexts[next_day] = {"day": next_day}
+            next_day = get_next_relevant_day(next_day + timedelta(days=1))
+    else:
+        day_contexts = {wanted_day: {"day": wanted_day}}
+
+    for day in day_contexts:
+        day_contexts[day]["substitutions"] = LessonSubstitution.objects.on_day(
+            day
+        ).order_by("lesson_period__lesson__groups", "lesson_period__period")
+
+    if not is_print:
+        context = day_contexts[wanted_day]
+        context["datepicker"] = {
+            "date": date_unix(wanted_day),
+            "dest": reverse("substitutions"),
+        }
+
+        context["url_prev"], context["url_next"] = get_prev_next_by_day(
+            wanted_day, "substitutions_by_date"
+        )
 
-    context["url_prev"], context["url_next"] = get_prev_next_by_day(
-        wanted_day, "substitutions_by_date"
-    )
+        template_name = "chronos/substitutions.html"
+    else:
+        context["days"] = day_contexts
+        template_name = "chronos/substitutions_print.html"
 
-    return render(request, "chronos/substitutions.html", context)
+    return render(request, template_name, context)