diff --git a/hypha/apply/funds/views.py b/hypha/apply/funds/views.py
index 011928529e05385d6e0afb973e4b69f4e74f9d74..96bb65f9f746f80d27bc7c319720d5fbd7ce9c1e 100644
--- a/hypha/apply/funds/views.py
+++ b/hypha/apply/funds/views.py
@@ -39,7 +39,7 @@ from hypha.apply.projects.forms import CreateProjectForm
 from hypha.apply.projects.models import Project
 from hypha.apply.review.views import ReviewContextMixin
 from hypha.apply.users.decorators import staff_required
-from hypha.apply.utils.pdfs import make_pdf
+from hypha.apply.utils.pdfs import draw_submission_content, make_pdf
 from hypha.apply.utils.storage import PrivateMediaView
 from hypha.apply.utils.views import (
     DelegateableListView,
@@ -1016,15 +1016,23 @@ class SubmissionDetailPDFView(SingleObjectMixin, View):
 
     def get(self, request, *args, **kwargs):
         self.object = self.get_object()
+        content = draw_submission_content(
+            self.object.output_text_answers()
+        )
         pdf = make_pdf(
             title=self.object.title,
-            meta=[
-                self.object.stage,
-                self.object.page,
-                self.object.round,
-                f"Lead: { self.object.lead }",
-            ],
-            content=self.object.output_text_answers()
+            sections=[
+                {
+                    'content': content,
+                    'title': 'Submission',
+                    'meta': [
+                        self.object.stage,
+                        self.object.page,
+                        self.object.round,
+                        f"Lead: { self.object.lead }",
+                    ],
+                },
+            ]
         )
         return FileResponse(
             pdf,
diff --git a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
index 6994f1a1f649a8d35958301a9842aaa7d74225e3..fef33c9dd193e04a3d94d68cb59044c379226551 100644
--- a/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
+++ b/hypha/apply/projects/templates/application_projects/includes/supporting_documents.html
@@ -15,7 +15,7 @@
             <div class="docs-block__row-inner">
                 <a class="docs-block__link" href="{% url 'apply:submissions:simplified' pk=project.submission.pk %}">View</a>
                 {% if not user.is_applicant %}
-                    <a class="docs-block__link" href="#">Download</a>
+                    <a class="docs-block__link" href="{% url "apply:submissions:download" pk=project.submission.pk %}">Download</a>
                 {% endif %}
             </div>
         </li>
diff --git a/hypha/apply/projects/templates/application_projects/project_simplified_detail.html b/hypha/apply/projects/templates/application_projects/project_simplified_detail.html
index 40cc585fb66f2dad1f7cc7fb406a96e15130dbd5..675d2228695479c6558592a3b35db070b460ac8c 100644
--- a/hypha/apply/projects/templates/application_projects/project_simplified_detail.html
+++ b/hypha/apply/projects/templates/application_projects/project_simplified_detail.html
@@ -17,6 +17,12 @@
                 <span>{{ object.submission.round }}</span>
                 <span>Lead: {{ object.lead }}</span>
             </h5>
+            <a
+                class="button button--primary simplified__button"
+                href="{% url "apply:projects:download" pk=object.pk %}"
+            >
+                Download PDF
+            </a>
         </div>
     </div>
 
diff --git a/hypha/apply/projects/tests/test_views.py b/hypha/apply/projects/tests/test_views.py
index c095677bc38d6980994e314a8cc2225c88f3dbdf..e26cbcd3d50ec052daaf753faf3f98b7a3ac8fdc 100644
--- a/hypha/apply/projects/tests/test_views.py
+++ b/hypha/apply/projects/tests/test_views.py
@@ -1529,3 +1529,40 @@ class TestSkipReport(BaseViewTestCase):
         self.assertEqual(response.status_code, 200)
         report.refresh_from_db()
         self.assertTrue(report.skipped)
+
+
+class TestStaffProjectPDFExport(BaseViewTestCase):
+    base_view_name = 'download'
+    url_name = 'funds:projects:{}'
+    user_factory = StaffFactory
+
+    def get_kwargs(self, instance):
+        return {
+            'pk': instance.pk,
+        }
+
+    def test_can_access(self):
+        project = ProjectFactory()
+        response = self.get_page(project)
+        self.assertEqual(response.status_code, 200)
+
+    def test_reponse_object_is_pdf(self):
+        project = ProjectFactory()
+        response = self.get_page(project)
+        self.assertEqual(response.filename, project.title + '.pdf')
+
+
+class ApplicantStaffProjectPDFExport(BaseViewTestCase):
+    base_view_name = 'download'
+    url_name = 'funds:projects:{}'
+    user_factory = ApplicantFactory
+
+    def get_kwargs(self, instance):
+        return {
+            'pk': instance.pk,
+        }
+
+    def test_cant_access(self):
+        project = ProjectFactory()
+        response = self.get_page(project)
+        self.assertEqual(response.status_code, 403)
diff --git a/hypha/apply/projects/urls.py b/hypha/apply/projects/urls.py
index 41d28497532b41fe68ee4e7d142d6ebc1ffb30a1..5e6784e6da160a5fe9d142f434427a7cfcb23a47 100644
--- a/hypha/apply/projects/urls.py
+++ b/hypha/apply/projects/urls.py
@@ -8,6 +8,7 @@ from .views import (
     PaymentRequestListView,
     PaymentRequestPrivateMedia,
     PaymentRequestView,
+    ProjectDetailPDFView,
     ProjectDetailSimplifiedView,
     ProjectDetailView,
     ProjectEditView,
@@ -31,6 +32,7 @@ urlpatterns = [
         path('edit/', ProjectEditView.as_view(), name="edit"),
         path('documents/<int:file_pk>/', ProjectPrivateMediaView.as_view(), name="document"),
         path('contract/<int:file_pk>/', ContractPrivateMediaView.as_view(), name="contract"),
+        path('download/', ProjectDetailPDFView.as_view(), name='download'),
         path('simplified/', ProjectDetailSimplifiedView.as_view(), name='simplified'),
         path('request/', CreatePaymentRequestView.as_view(), name='request'),
     ])),
diff --git a/hypha/apply/projects/views/project.py b/hypha/apply/projects/views/project.py
index f90774d2f882a253a9442c3383056243d4aca24a..d9bc8fd3ef129f9846e1a64efcdfcca8d5b7650b 100644
--- a/hypha/apply/projects/views/project.py
+++ b/hypha/apply/projects/views/project.py
@@ -6,7 +6,7 @@ from django.contrib.auth.mixins import UserPassesTestMixin
 from django.core.exceptions import PermissionDenied
 from django.db import transaction
 from django.db.models import Count
-from django.http import Http404
+from django.http import FileResponse, Http404
 from django.shortcuts import get_object_or_404, redirect
 from django.urls import reverse, reverse_lazy
 from django.utils import timezone
@@ -14,6 +14,7 @@ from django.utils.decorators import method_decorator
 from django.utils.functional import cached_property
 from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext_lazy as _
+from django.views import View
 from django.views.generic import (
     CreateView,
     DetailView,
@@ -21,12 +22,18 @@ from django.views.generic import (
     TemplateView,
     UpdateView,
 )
+from django.views.generic.detail import SingleObjectMixin
 from django_filters.views import FilterView
 from django_tables2 import SingleTableMixin
 
 from hypha.apply.activity.messaging import MESSAGES, messenger
 from hypha.apply.activity.views import ActivityContextMixin, CommentFormView
 from hypha.apply.users.decorators import approver_required, staff_required
+from hypha.apply.utils.pdfs import (
+    draw_project_content,
+    draw_submission_content,
+    make_pdf,
+)
 from hypha.apply.utils.storage import PrivateMediaView
 from hypha.apply.utils.views import DelegateableView, DelegatedViewMixin, ViewDispatcher
 
@@ -518,6 +525,52 @@ class ProjectDetailSimplifiedView(DetailView):
     template_name_suffix = '_simplified_detail'
 
 
+@method_decorator(staff_required, name='dispatch')
+class ProjectDetailPDFView(SingleObjectMixin, View):
+    model = Project
+
+    def get(self, request, *args, **kwargs):
+        self.object = self.get_object()
+        response = ProjectDetailSimplifiedView.as_view()(
+            request=self.request,
+            pk=self.object.pk,
+        )
+        project = draw_project_content(
+            response.render().content
+        )
+        submission = draw_submission_content(
+            self.object.submission.output_text_answers()
+        )
+        pdf = make_pdf(
+            title=self.object.title,
+            sections=[
+                {
+                    'content': project,
+                    'title': 'Project Approval Form',
+                    'meta': [
+                        self.object.submission.page,
+                        self.object.submission.round,
+                        f"Lead: { self.object.lead }",
+                    ],
+                }, {
+                    'content': submission,
+                    'title': 'Submission',
+                    'meta': [
+                        self.object.submission.stage,
+                        self.object.submission.page,
+                        self.object.submission.round,
+                        f"Lead: { self.object.submission.lead }",
+                    ],
+                },
+            ],
+        )
+        return FileResponse(
+            pdf,
+            as_attachment=True,
+            filename=self.object.title + '.pdf',
+        )
+
+
 class ProjectApprovalEditView(UpdateView):
     form_class = ProjectApprovalForm
     model = Project
diff --git a/hypha/apply/utils/pdfs.py b/hypha/apply/utils/pdfs.py
index 3e63b38f23ce98a85c754a757af5bf83acde0cc4..1c007f6a97d869220847e46616d87926c00d8258 100644
--- a/hypha/apply/utils/pdfs.py
+++ b/hypha/apply/utils/pdfs.py
@@ -1,5 +1,6 @@
 import io
 import os
+from itertools import cycle
 
 from bs4 import BeautifulSoup, NavigableString
 from reportlab.lib import pagesizes
@@ -9,18 +10,23 @@ from reportlab.lib.utils import simpleSplit
 from reportlab.pdfbase import pdfmetrics
 from reportlab.pdfbase.ttfonts import TTFont
 from reportlab.platypus import (
+    BaseDocTemplate,
+    Frame,
     KeepTogether,
     ListFlowable,
     ListItem,
+    NextPageTemplate,
+    PageBreak,
+    PageTemplate,
     Paragraph,
-    SimpleDocTemplate,
     Spacer,
     Table,
     TableStyle,
 )
 
-styles = {
+STYLES = {
     'Question': PS(fontName='MontserratBold', fontSize=14, name='Question', spaceAfter=0, spaceBefore=18, leading=21),
+    'QuestionSmall': PS(fontName='MontserratBold', fontSize=12, name='QuestionSmall', spaceAfter=0, spaceBefore=16, leading=18),
     'Normal': PS(fontName='NotoSans', name='Normal'),
     'Heading1': PS(fontName='NotoSansBold', fontSize=12, name='Heading1', spaceAfter=4, spaceBefore=12, leading=18),
     'Heading2': PS(fontName='NotoSansBold', fontSize=10, name='Heading2', spaceAfter=4, spaceBefore=10, leading=15),
@@ -77,31 +83,61 @@ PAGE_WIDTH, PAGE_HEIGHT = pagesizes.legal
 FRAME_PADDING = 6
 
 
-def make_pdf(title, meta, content):
+def do_nothing(doc, canvas):
+    pass
+
+
+class ReportDocTemplate(BaseDocTemplate):
+    def build(self, flowables, onFirstPage=do_nothing, onLaterPages=do_nothing):
+        frame = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
+        self.addPageTemplates([
+            PageTemplate(id='Header', autoNextPageTemplate='Main', frames=frame, onPage=onFirstPage, pagesize=self.pagesize),
+            PageTemplate(id='Main', frames=frame, onPage=onLaterPages, pagesize=self.pagesize),
+        ])
+        super().build(flowables)
+
+
+def make_pdf(title, sections):
     prepare_fonts()
     buffer = io.BytesIO()
-    doc = SimpleDocTemplate(
+
+    doc = ReportDocTemplate(
         buffer,
         pagesize=(PAGE_WIDTH, PAGE_HEIGHT),
         title=title,
     )
 
-    blocks = []
-    extra_content = draw_content(content)
-    blocks = [*blocks, *extra_content]
+    story = []
+    for section in sections:
+        story.extend(section['content'])
+        story.append(NextPageTemplate('Header'))
+        story.append(PageBreak())
+
+    current_section = None
+    sections = cycle(sections)
 
-    def title_page(canvas, doc):
+    def header_page(canvas, doc):
+        nonlocal current_section
+        current_section = next(sections)
         canvas.saveState()
-        title_spacer = draw_title_block(canvas, doc, title, meta)
+        title_spacer = draw_title_block(
+            canvas,
+            doc,
+            current_section['title'],
+            title,
+            current_section['meta'],
+        )
         canvas.restoreState()
-        blocks.insert(0, title_spacer)
+        story.insert(0, title_spacer)
 
-    def later_page(canvas, doc):
+    def main_page(canvas, doc):
+        nonlocal current_section
         canvas.saveState()
-        draw_header(canvas, doc, title)
+        spacer = draw_header(canvas, doc, current_section['title'], title)
+        story.insert(0, spacer)
         canvas.restoreState()
 
-    doc.build(blocks, onFirstPage=title_page, onLaterPages=later_page)
+    doc.build(story, onFirstPage=header_page, onLaterPages=main_page)
 
     buffer.seek(0)
     return buffer
@@ -111,32 +147,48 @@ def split_text(canvas, text, width):
     return simpleSplit(text, canvas._fontname, canvas._fontsize, width)
 
 
-def draw_header(canvas, doc, title):
+def draw_header(canvas, doc, page_title, title):
     title_size = 10
 
+    # Set canvas font to correctly calculate the splitting
+    canvas.setFont("MontserratBold", title_size)
+
+    text_width = PAGE_WIDTH - doc.leftMargin - doc.rightMargin - 2 * FRAME_PADDING
+    split_title = split_text(canvas, title, text_width)
+
+    # only count title - assume 1 line of title in header
+    total_height = (
+        doc.topMargin +
+        1.5 * (len(split_title) - 1) * title_size +
+        title_size / 2  # bottom padding
+    )
+
     canvas.setFillColor(DARK_GREY)
     canvas.rect(
         0,
-        PAGE_HEIGHT - doc.topMargin,
+        PAGE_HEIGHT - total_height,
         PAGE_WIDTH,
-        doc.topMargin,
+        total_height,
         stroke=False,
         fill=True,
     )
-    # Set canvas font to correctly calculate the splitting
-
-    canvas.setFont("MontserratBold", title_size)
-    canvas.setFillColor(white)
-
-    text_width = PAGE_WIDTH - doc.leftMargin - doc.rightMargin - 2 * FRAME_PADDING
-    split_title = split_text(canvas, title, text_width)
 
     pos = (
         (PAGE_HEIGHT - doc.topMargin) +  # bottom of top margin
-        1.5 * len(split_title) * title_size +  # text
-        title_size / 2  # bottom padding
+        title_size / 2 +  # spacing below page title
+        1.5 * 1 * title_size  # text
+    )
+
+    canvas.setFillColor(white)
+
+    canvas.drawString(
+        doc.leftMargin + FRAME_PADDING,
+        pos,
+        page_title,
     )
 
+    pos -= title_size / 2
+
     for line in split_title:
         pos -= title_size
         canvas.drawString(
@@ -146,8 +198,11 @@ def draw_header(canvas, doc, title):
         )
         pos -= title_size / 2
 
+    return Spacer(1, total_height - doc.topMargin)
+
 
-def draw_title_block(canvas, doc, title, meta):
+def draw_title_block(canvas, doc, page_title, title, meta):
+    page_title_size = 20
     title_size = 30
     meta_size = 10
 
@@ -158,10 +213,16 @@ def draw_title_block(canvas, doc, title, meta):
     canvas.setFillColor(white)
     split_title = split_text(canvas, title, text_width)
 
+    canvas.setFont("MontserratBold", meta_size)
+    canvas.setFillColor(white)
+    meta_text = '  |  '.join(str(text) for text in meta)
+    split_meta = split_text(canvas, meta_text, text_width)
+
     total_height = (
         doc.topMargin +
+        page_title_size + page_title_size * 3 / 4 +  # page title + spaceing
         len(split_title) * (title_size + title_size / 2) +  # title + spacing
-        meta_size * 4  # 1 for text 3 for spacing
+        (1.5 * len(split_meta) + 3) * meta_size  # 1.5 per text line + 3 for spacing
     )
 
     canvas.setFillColor(DARK_GREY)
@@ -174,10 +235,20 @@ def draw_title_block(canvas, doc, title, meta):
         fill=True,
     )
 
-    canvas.setFont("MontserratBold", title_size)
+    canvas.setFont("MontserratBold", page_title_size)
     canvas.setFillColor(white)
     pos = PAGE_HEIGHT - doc.topMargin
+    pos -= page_title_size
+    canvas.drawString(
+        doc.leftMargin + FRAME_PADDING,
+        pos,
+        page_title,
+    )
 
+    pos -= page_title_size * 3 / 4
+
+    canvas.setFont("MontserratBold", title_size)
+    canvas.setFillColor(white)
     for line in split_title:
         pos -= title_size
         canvas.drawString(
@@ -189,20 +260,28 @@ def draw_title_block(canvas, doc, title, meta):
 
     canvas.setFont("MontserratBold", meta_size)
     canvas.setFillColor(white)
-    meta_text = '  |  '.join(str(text) for text in meta)
 
     pos -= meta_size * 2
-    canvas.drawString(
-        doc.leftMargin + FRAME_PADDING,
-        pos,
-        meta_text,
-    )
+
+    for line in split_meta:
+        canvas.drawString(
+            doc.leftMargin + FRAME_PADDING,
+            pos,
+            line,
+        )
+        pos -= meta_size / 2
 
     return Spacer(1, total_height - doc.topMargin)
 
 
-def handle_block(block):
+def handle_block(block, custom_style=None):
     paragraphs = []
+    if not custom_style:
+        custom_style = {}
+
+    styles = {**STYLES}
+    for style, overwrite in custom_style.items():
+        styles[style] = STYLES[overwrite]
 
     for tag in block:
         if isinstance(tag, NavigableString):
@@ -243,6 +322,7 @@ def handle_block(block):
                 )
             )
         else:
+            style = None
             if tag.name in {'p'}:
                 style = styles['Normal']
             elif tag.name == 'h2':
@@ -254,18 +334,22 @@ def handle_block(block):
             elif tag.name == 'h5':
                 style = styles['Heading5']
 
-            text = tag.get_text()
-            if text:
-                paragraphs.append(Paragraph(text, style))
+            if style:
+                text = tag.get_text()
+                if text:
+                    paragraphs.append(Paragraph(text, style))
+            else:
+                paragraphs.extend(handle_block(tag))
     return paragraphs
 
 
-def draw_content(content):
+def draw_submission_content(content):
+    prepare_fonts()
     paragraphs = []
 
     for section in BeautifulSoup(content, "html5lib").find_all('section'):
         question_text = section.select_one('.question').get_text()
-        question = Paragraph(question_text, styles['Question'])
+        question = Paragraph(question_text, STYLES['Question'])
 
         # Keep the question and the first block of the answer together
         # this keeps 1 line answers tidy and ensures that bigger responses break
@@ -279,3 +363,13 @@ def draw_content(content):
             *rest
         ])
     return paragraphs
+
+
+def draw_project_content(content):
+    prepare_fonts()
+    paragraphs = []
+    for section in BeautifulSoup(content, "html5lib").find_all(class_='simplified__wrapper'):
+        flowables = handle_block(section, custom_style={"Heading3": "Question", "Heading5": "QuestionSmall"})
+        paragraphs.extend(flowables)
+
+    return paragraphs