diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b6443616ef0e981041c82f7c2c11be428f85b898..b1000af70c49f45a1adf510bc3e0be703292bea6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -16,6 +16,11 @@ If you're upgrading from 3.x, there is now a migration path to use.
 Therefore, please install ``AlekSIS-App-Lesrooster`` which now
 includes parts of the legacy Chronos and the migration path.
 
+Added
+~~~~~
+
+* Printout with person overview including all statistics.
+
 `4.0.0.dev9`_ - 2024-12-07
 --------------------------
 
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue
index 8a9058d6d1a8391d6b72db9bb239e66afcefa9a8..52444d0e5b5b03a72bbcb631bc7d75ed28427fb1 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/CoursebookPrintDialog.vue
@@ -157,7 +157,7 @@ export default {
     },
     print() {
       this.$router.push({
-        name: "alsijil.coursebook_print",
+        name: "alsijil.coursebookPrintGroups",
         params: {
           groupIds: this.selectedGroups,
         },
diff --git a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue
index c9c8bcd3ce8d762c9fe091f343adda5307cbfe8b..84e63a49529e9536c550616d4738f856232db104 100644
--- a/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue
+++ b/aleksis/apps/alsijil/frontend/components/coursebook/statistics/StatisticsForPersonPage.vue
@@ -257,8 +257,16 @@
         v-model="$root.activeSchoolTerm"
         color="secondary"
       />
-      <!-- TODO: add functionality -->
-      <v-btn v-if="toolbar" icon color="primary" disabled>
+      <v-btn
+        v-if="toolbar"
+        icon
+        color="primary"
+        :to="{
+          name: 'alsijil.coursebookPrintPerson',
+          params: { id: personId },
+        }"
+        target="_blank"
+      >
         <v-icon>$print</v-icon>
       </v-btn>
       <FabButton v-else icon-text="$print" i18n-key="actions.print" disabled />
diff --git a/aleksis/apps/alsijil/frontend/index.js b/aleksis/apps/alsijil/frontend/index.js
index 18e0f68eaca178cbaf3b358a8065257cc3e745b3..13967281fe1de32919000b48ecadef95304e2ef3 100644
--- a/aleksis/apps/alsijil/frontend/index.js
+++ b/aleksis/apps/alsijil/frontend/index.js
@@ -95,7 +95,15 @@ export default {
     {
       path: "print/groups/:groupIds+/",
       component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
-      name: "alsijil.coursebook_print",
+      name: "alsijil.coursebookPrintGroups",
+      props: {
+        byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+      },
+    },
+    {
+      path: "print/person/:id(\\d+)?/",
+      component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+      name: "alsijil.coursebookPrintPerson",
       props: {
         byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
       },
diff --git a/aleksis/apps/alsijil/tasks.py b/aleksis/apps/alsijil/tasks.py
index 355396dcb91fdb1436df8ec0b62d78dadde87066..47fd9074c4ad6fd81383089cf2563d2691ee3ba4 100644
--- a/aleksis/apps/alsijil/tasks.py
+++ b/aleksis/apps/alsijil/tasks.py
@@ -9,7 +9,7 @@ from celery.states import SUCCESS
 
 from aleksis.apps.cursus.models import Course
 from aleksis.apps.kolego.models.absence import AbsenceReason
-from aleksis.core.models import Group, PDFFile
+from aleksis.core.models import Group, PDFFile, Person, SchoolTerm
 from aleksis.core.util.celery_progress import ProgressRecorder, recorded_task
 from aleksis.core.util.pdf import generate_pdf_from_template
 
@@ -18,7 +18,7 @@ from .util.statistics import StatisticsBuilder
 
 
 @recorded_task
-def generate_full_register_printout(
+def generate_groups_register_printout(
     groups: list[int],
     file_object: int,
     recorder: ProgressRecorder,
@@ -138,3 +138,66 @@ def generate_full_register_printout(
             raise Exception(_("PDF generation failed"))
 
     recorder.set_progress(5 + len(groups), _number_of_steps)
+
+
+@recorded_task
+def generate_person_register_printout(
+    person: int,
+    school_term: int,
+    file_object: int,
+    recorder: ProgressRecorder,
+):
+    """Generate a register printout as PDF for a person."""
+
+    context = {}
+
+    _number_of_steps = 4
+
+    recorder.set_progress(1, _number_of_steps, _("Loading data ..."))
+
+    person = Person.objects.get(pk=person)
+    school_term = SchoolTerm.objects.get(pk=school_term)
+
+    doc_query_set = Documentation.objects.select_related("subject").prefetch_related("teachers")
+
+    statistics = (
+        (
+            StatisticsBuilder(Person.objects.filter(id=person.id))
+            .use_from_school_term(school_term)
+            .annotate_statistics()
+        )
+        .prefetch_relevant_participations(documentation_with_details=doc_query_set)
+        .prefetch_relevant_personal_notes(documentation_with_details=doc_query_set)
+        .build()
+        .first()
+    )
+
+    context["person"] = statistics
+
+    context["school_term"] = school_term
+
+    context["absence_reasons"] = AbsenceReason.objects.filter(
+        tags__short_name="class_register", count_as_absent=True
+    )
+    context["absence_reasons_not_counted"] = AbsenceReason.objects.filter(
+        tags__short_name="class_register", count_as_absent=False
+    )
+    context["extra_marks"] = ExtraMark.objects.all()
+
+    recorder.set_progress(2, _number_of_steps, _("Generating template ..."))
+
+    file_object, result = generate_pdf_from_template(
+        "alsijil/print/register_for_person.html",
+        context,
+        file_object=PDFFile.objects.get(pk=file_object),
+    )
+
+    recorder.set_progress(3, _number_of_steps, _("Generating PDF ..."))
+
+    with allow_join_result():
+        result.wait()
+        file_object.refresh_from_db()
+        if not result.status == SUCCESS and file_object.file:
+            raise Exception(_("PDF generation failed"))
+
+    recorder.set_progress(4, _number_of_steps)
diff --git a/aleksis/apps/alsijil/templates/alsijil/print/register_for_group.html b/aleksis/apps/alsijil/templates/alsijil/print/register_for_group.html
index 2257633273a5faccede0e6bef2abbb059a2f65b5..8e395b7239765b60c0bfa8cd5d4db5a00bb2671b 100644
--- a/aleksis/apps/alsijil/templates/alsijil/print/register_for_group.html
+++ b/aleksis/apps/alsijil/templates/alsijil/print/register_for_group.html
@@ -42,7 +42,7 @@
 
     {% if include_person_overviews %}
       {% for person in group.members_with_stats %}
-        {% include "alsijil/partials/person_overview.html" with person=person group=group %}
+        {% include "alsijil/partials/person_overview.html" with person=person %}
         <div class="page-break">&nbsp;</div>
       {% endfor %}
     {% endif %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/print/register_for_person.html b/aleksis/apps/alsijil/templates/alsijil/print/register_for_person.html
new file mode 100644
index 0000000000000000000000000000000000000000..f22d62060b79f8c416e461ecdadabe303738ae43
--- /dev/null
+++ b/aleksis/apps/alsijil/templates/alsijil/print/register_for_person.html
@@ -0,0 +1,15 @@
+{% extends "core/base_print.html" %}
+
+{% load static i18n %}
+
+{% block page_title %}
+  {% trans "Class Register" %} · {{ school_term.name }}
+{% endblock %}
+
+{% block extra_head %}
+  <link rel="stylesheet" href="{% static 'css/alsijil/full_register.css' %}"/>
+{% endblock %}
+
+{% block content %}
+      {% include "alsijil/partials/person_overview.html" with person=person %}
+{% endblock %}
diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py
index 8017db1c94d063930ed462be021846841bce3d40..e0dfc61b044cdbe28dac8d03680dc132e8b4665d 100644
--- a/aleksis/apps/alsijil/urls.py
+++ b/aleksis/apps/alsijil/urls.py
@@ -3,7 +3,10 @@ from django.urls import path
 from . import views
 
 urlpatterns = [
-    path("print/groups/<path:ids>/", views.full_register_for_group, name="full_register_for_group"),
+    path(
+        "print/groups/<path:ids>/", views.groups_register_printout, name="full_register_for_group"
+    ),
+    path("print/person/<int:pk>/", views.person_register_printout, name="full_register_for_person"),
     path("group_roles/", views.GroupRoleListView.as_view(), name="group_roles"),
     path("group_roles/create/", views.GroupRoleCreateView.as_view(), name="create_group_role"),
     path(
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index 71b4cbdd6ee85c3eb280c9f15549e31736b060ab..3e68392033b4bb56e241583f704657563222f108 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -22,10 +22,10 @@ from aleksis.core.mixins import (
     AdvancedEditView,
     SuccessNextMixin,
 )
-from aleksis.core.models import Group, PDFFile
+from aleksis.core.models import Group, PDFFile, Person
 from aleksis.core.util import messages
 from aleksis.core.util.celery_progress import render_progress_page
-from aleksis.core.util.core_helpers import has_person
+from aleksis.core.util.core_helpers import get_active_school_term, has_person
 
 from .forms import (
     AssignGroupRoleForm,
@@ -36,10 +36,10 @@ from .models import GroupRole, GroupRoleAssignment
 from .tables import (
     GroupRoleTable,
 )
-from .tasks import generate_full_register_printout
+from .tasks import generate_groups_register_printout, generate_person_register_printout
 
 
-def full_register_for_group(request: HttpRequest, ids: str) -> HttpResponse:
+def groups_register_printout(request: HttpRequest, ids: str) -> HttpResponse:
     """Show a configurable register printout as PDF for a group."""
 
     def parse_get_param(name):
@@ -65,7 +65,7 @@ def full_register_for_group(request: HttpRequest, ids: str) -> HttpResponse:
 
     redirect_url = f"/pdfs/{file_object.pk}"
 
-    result = generate_full_register_printout.delay(
+    result = generate_groups_register_printout.delay(
         groups=ids,
         file_object=file_object.pk,
         include_cover=parse_get_param("cover"),
@@ -95,6 +95,43 @@ def full_register_for_group(request: HttpRequest, ids: str) -> HttpResponse:
     )
 
 
+def person_register_printout(request: HttpRequest, pk: int) -> HttpResponse:
+    """Show a statistics printout as PDF for a person."""
+
+    person = get_object_or_404(Person, pk=pk)
+    school_term = get_active_school_term(request)
+    if not request.user.has_perm("alsijil.view_person_statistics_rule", person) or not school_term:
+        raise PermissionDenied()
+
+    file_object = PDFFile.objects.create()
+    file_object.person = request.user.person
+    file_object.save()
+
+    redirect_url = f"/pdfs/{file_object.pk}"
+
+    result = generate_person_register_printout.delay(
+        person=person.id,
+        school_term=school_term.id,
+        file_object=file_object.pk,
+    )
+
+    back_url = request.GET.get("back", "")
+
+    return render_progress_page(
+        request,
+        result,
+        title=_(f"Generate register printout for {person.full_name}"),
+        progress_title=_("Generate register printout …"),
+        success_message=_("The printout has been generated successfully."),
+        error_message=_("There was a problem while generating the printout."),
+        redirect_on_success_url=redirect_url,
+        back_url=back_url,
+        button_title=_("Download PDF"),
+        button_url=redirect_url,
+        button_icon="picture_as_pdf",
+    )
+
+
 @method_decorator(pwa_cache, "dispatch")
 class GroupRoleListView(PermissionRequiredMixin, SingleTableView):
     """Table of all group roles."""