TransWikia.com

Merging two PDF generating views in Django

Stack Overflow Asked by Piethon on August 8, 2020

I have two working PDF generating views. I would like to create a view that combines these two views & merges the output PDFs produced by the two views to generate one PDF containing the output PDFs of the two views.
I would also like to specify that these two views use different approaches to generate PDFs. The first one renders an HTML template to create PDF & the second creates a PDF from scratch using Reportlab.
I am new to Django, How can I do this?

View 1

class GenerateAllDocs(View):
    def allDocGen(request):
        if request.method == 'POST':
            all_doc_gen_form = GenerateAllForms(request.POST)
            if all_doc_gen_form.is_valid():

                some_Field_1= all_doc_gen_form.cleaned_data['some_Field_1']
                some_Field_2= all_doc_gen_form.cleaned_data['some_Field_2']


                template = get_template('PDF_templates/att_pg_pdf_template.html')

                data = {
                    'some_Field_1': some_Field_1,
                    'some_Field_2': some_Field_2,

                }

                html = template.render(data)
                pdf = render_to_pdf('PDF_templates/att_pg_pdf_template.html', data)
                if pdf:
                    response = HttpResponse(pdf, content_type = 'application/pdf')
                    filename = "something - %s.pdf" %(data.get('zzzzz'))
                    content = "inline; filename=%s" %(filename)
                    download = request.GET.get('download')
                    if download:
                        content = "attachment; filename%s" %(filename)
                    response['Content-Disposition'] = content
                    return response
                return HttpResponse('Not Found')


        all_doc_gen_form = GenerateAllForms()
        return render(request, 'form_UI_templates/pg_att_form_UI_template.html', {'all_doc_gen_form':all_doc_gen_form})

View 2

def template_PDF_view(request):

    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; inline; filename="somefilename.pdf"'

    buffer = BytesIO()

    # Create the PDF object, using the BytesIO object as its "file."
    p = canvas.Canvas(buffer, pagesize=letter)

    # Draw things on the PDF. Here's where the PDF generation happens.


    p.drawImage('D:/worrk/PyDjango/mysite - PROD VERSION/main/static/images/cerfaImg.jpg',0,0, width=8.27 * inch, height= 11.69 * inch)

    p.drawString(40, 724, "  ".join('XX-XXX-XX'.replace('-','').upper())) # car_licence_plate
    p.drawString(193, 149, 'hello 123') # address_city      

    # Close the PDF object cleanly.
    p.showPage()
    p.save()

    # Get the value of the BytesIO buffer and write it to the response.
    pdf = buffer.getvalue()
    buffer.close()
    response.write(pdf)
    return response

EDIT #1
Following suggestion by ktowen, here is my updated code. I created a function based view for my view 2 and call that function inside of view 1 & then attempt to merge the two PDFs created by the views. This still doesn’t work – error message is written after code.

def create_cerfa(request):
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
    buffer_1 = BytesIO()

    p = canvas.Canvas(buffer_1)
    p.drawImage('D:/worrk/PyDjango/mysite - PROD VERSION/main/static/images/cerfaImg.jpg',0,0, width=8.27 * inch, height= 11.69 * inch)
    p.drawString(40, 724, "  ".join('AZ-343-BT'.replace('-','').upper()))
    p.drawString(178, 724, "  ".join('VF77JNFUC9J177958').upper())
    p.save()
    
    pdf = buffer_1.getvalue()
    buffer_1.close()
    response.write(pdf)
    return response

def createAttestation(request):
    if request.method == 'POST':
        all_doc_gen_form = GenerateAllForms(request.POST)
        if all_doc_gen_form.is_valid():

            data1 = all_doc_gen_form.cleaned_data['data1']
            data12 = all_doc_gen_form.cleaned_data['data12']

            template = get_template('PDF_templates/att_pg_pdf_template.html')

            data = {
                'data1': data1,
                'data12': data12,

            }

            html = template.render(data)
            pdf = render_to_pdf('PDF_templates/att_pg_pdf_template.html', data)

            cerfa = create_cerfa(request)

            pdf1resp = HttpResponse(pdf, content_type = 'application/pdf')
            pdf2resp = HttpResponse(cerfa, content_type = 'application/pdf')

            pdfs = [pdf1resp, pdf2resp]

            merger = PdfFileMerger()

            for item in pdfs:
                merger.append(item)

            merger.write()
    
            if pdf:
                response = HttpResponse(merger, content_type = 'application/pdf')
                return response

    all_doc_gen_form = GenerateAllForms()
    return render(request, 'form_UI_templates/pg_att_form_UI_template.html', {'all_doc_gen_form':all_doc_gen_form})

Error Message

Traceback (most recent call last):
  File "C:UsersWorkAppDataLocalProgramsPythonPython38-32libsite-packagesdjangocorehandlersexception.py", line 34, in inner
    response = get_response(request)
  File "C:UsersWorkAppDataLocalProgramsPythonPython38-32libsite-packagesdjangocorehandlersbase.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:UsersWorkAppDataLocalProgramsPythonPython38-32libsite-packagesdjangocorehandlersbase.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:worrkPyDjangomysite - PROD VERSIONmainviews.py", line 460, in createAttestation
    merger.append(item)
  File "C:UsersWorkAppDataLocalProgramsPythonPython38-32libsite-packagesPyPDF2merger.py", line 203, in append
    self.merge(len(self.pages), fileobj, bookmark, pages, import_bookmarks)
  File "C:UsersWorkAppDataLocalProgramsPythonPython38-32libsite-packagesPyPDF2merger.py", line 133, in merge
    pdfr = PdfFileReader(fileobj, strict=self.strict)
  File "C:UsersWorkAppDataLocalProgramsPythonPython38-32libsite-packagesPyPDF2pdf.py", line 1084, in __init__
    self.read(stream)
  File "C:UsersWorkAppDataLocalProgramsPythonPython38-32libsite-packagesPyPDF2pdf.py", line 1689, in read
    stream.seek(-1, 2)

Exception Type: AttributeError at /tst2/
Exception Value: 'HttpResponse' object has no attribute 'seek'

EDIT #2

So I’ve narrowed down the problem to the actual code of merging the PDFs. I tested the approach given in EDIT 2 by ktowen.

I commented the merging part and then returned the PDFs as follows

return FileResponse(pdf1, as_attachment=True, content_type='application/pdf')

I checked it with PDF1 & PDF2 – Both are getting generated, but I don’t understand what is wrong with the merging part? & how can I fix it?

EDIT #3
Here’s my Render to PDF function

from __future__ import print_function
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
    
def render_to_pdf(template_src, context_dict={}):
        template = get_template(template_src)
        html  = template.render(context_dict)
        result = BytesIO()
        pdf = pisa.pisaDocument(BytesIO(html.encode("ISO-8859-1")), result)
        if not pdf.err:
            return HttpResponse(result.getvalue(), content_type='application/pdf')
        return None

One Answer

You can extract the pdf generation logic to two functions and use PyPDF2 PdfFileMerger to merge the two pdfs.

This is the general idea:

from PyPDF2 import PdfFileMerger

def merged_PDF_view(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'attachment; inline; filename="somefilename.pdf"'

    pdf1_buffer = get_pdf1()
    pdf2_buffer = get_pdf2()

    pdf_merger = PdfFileMerger()
    pdf_merger.append(pdf1_buffer)
    pdf_merger.append(pdf2_buffer)

    # This can probably be improved 
    pdf_merger.write(buffer)
    pdf_merger.close()
    buffer.seek(0)

    response.write(buffer.getvalue())
    return response
    

EDIT 1 Try with this

class WillThisWork(View): # I hope so :|
    def merged_PDF(request):

        buffer = BytesIO()

        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = 'attachment; inline; filename="somefilename.pdf"'

        pdf1_buffer = createAttestation(request)
        pdf2_buffer = create_cerfa(request)

        pdf_merger = PdfFileMerger()
        pdf_merger.append(pdf1_buffer)
        pdf_merger.append(pdf2_buffer)

        pdf_merger.write(buffer)
        pdf_merger.close()
        buffer.seek(0)

        return FileResponse(buffer, as_attachment=True, filename='hello.pdf')

EDIT 2 Based in your edit try this

def create_cerfa_pdf():
    filestream = BytesIO()

    p = canvas.Canvas(filestream)
    p.drawImage('D:/worrk/PyDjango/mysite - PROD VERSION/main/static/images/cerfaImg.jpg',0,0, width=8.27 * inch, height= 11.69 * inch)
    p.drawString(40, 724, "  ".join('AZ-343-BT'.replace('-','').upper()))
    p.drawString(178, 724, "  ".join('VF77JNFUC9J177958').upper())
    p.save()
    
    filestream.seek(0)
    return filestream

def create_cerfa(request):
    pdf = create_cerfa_pdf()
    return FileResponse(pdf, as_attachment=True, filename="somefilename.pdf")

def createAttestation(request):
    if request.method == 'POST':
        all_doc_gen_form = GenerateAllForms(request.POST)
        if all_doc_gen_form.is_valid():

            data1 = all_doc_gen_form.cleaned_data['data1']
            data12 = all_doc_gen_form.cleaned_data['data12']
            template = get_template('PDF_templates/att_pg_pdf_template.html')
            data = {
                'data1': data1,
                'data12': data12,
            }
            html = template.render(data)

            pdf1 = render_to_pdf('PDF_templates/att_pg_pdf_template.html', data)
            pdf2 = create_cerfa_pdf()

            merger = PdfFileMerger()
            for item in [pdf1, pdf2]:
                merger.append(item)

            filestream = BytesIO()
            merger.write(filestream)
            merger.close()
            filestream.seek(0)
    
            return FileResponse(filestream, as_attachment=True)

    all_doc_gen_form = GenerateAllForms()
    return render(request, 'form_UI_templates/pg_att_form_UI_template.html', {'all_doc_gen_form':all_doc_gen_form})

Answered by ktowen on August 8, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP