Search code examples
pythondjangopdf-generation

Is there a way of using FPDF to generate a PDF file from forms in Django?


I am trying to generate a PDF file and give it a custom name using Django 4.0.2 I have 2 inputs, one for the name and one for the photos, I want to be able to generate the PDF without saving it in my database but return it back to the user. I am using no models for this, plain html and python. I have the inputs:

<input
                    type="text"
                    id="inputPassword6"
                    class="form-control"
                    aria-describedby="passwordHelpInline"
                    name="name"
                />
<input
                    class="form-control"
                    type="file"
                    id="formFileMultiple"
                    multiple
                    accept=".png, .jpeg, .jpg"
                    name="photos"
                />
        <div class="d-grid gap-2 mt-3">
            <button class="btn btn-success" type="submit">Save PDF</button>
        </div>

And I am trying to merge and return the PDF file like this:

    if request.method == "POST" or "FILES":
        name = request.POST["name"]
        photos = request.FILES["photos"]

        # Convert photos to PDF
        pdf = FPDF()
        # imagelist is the list with all image filenames
        for image in photos:
            pdf.add_page()
            pdf.image(image, 0,0,210,297)
        pdf.output(f"{name} AIA 114.pdf", "F")

Current error:

Exception Type: TypeError
Exception Value:    
argument should be integer or bytes-like object, not 'str'

It looks like FPDF can not look into the image I provided. I tried to decode the image, but I hit another error:

'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

Can someone help me solve this problem or propose another way of doing it?


Solution

  • The solution that did work for me using no models is below, however this is hardcoded because I found out later, that I could have used default_storage and ContentFile inbuilt in django because the files are already in memory (InMemoryFile).

    def index(request):
        if request.method == "GET":
            return render(request, 'index.html')
    
        if request.method == "POST" or "FILES":
            try:
                # Get data from inputs
                name = request.POST["name"]
                photos = request.FILES.getlist('photos')
    
                count = 1
                if (len(photos) > 1):
                    # For multiple photos
                    imglist = []
                    counter = 0
    
                    # Write buffers and append to the list
                    for photo in photos:
                        bytes = photo.read() # Read files in memory
                        path = django_settings.MEDIA_ROOT + f"buffer{counter}.png" # auto path
                        counter = counter + 1 # counter
                        file = open(path, 'wb')
                        file.write(bytes)
                        file.close()
                        imglist.append(file)
    
                    # CONVERT
                    listconv = []
                    counter = 0
                    for img in imglist:
                        path = django_settings.MEDIA_ROOT + f"buffer{counter}.png"
                        img = Image.open(path)
                        img = img.convert('RGB')
                        listconv.append(img)
                        counter=counter+1
                    img.save(django_settings.MEDIA_ROOT + f"{name}.pdf", save_all=True, append_images=listconv[:-1])
    
                    # Return response
                    img = open(django_settings.MEDIA_ROOT + f"{name}.pdf", 'rb')
                    response = FileResponse(img)
                    return response
    
                elif (len(photos) == 1):
                    # For single photo
                    photos = request.FILES["photos"]
                    # WRITE FILE
                    bytes = photos.read() # CONTENT UPLOADED FILE
                    path = django_settings.MEDIA_ROOT + "buffer"
                    file=open(path,'wb')
                    file.write(bytes)
                    file.close()
    
                    # CONVERT
                    image1 = Image.open(path)
                    im1 = image1.convert('RGB')
                    im1.save(django_settings.MEDIA_ROOT + f"{name}.pdf")
                    img = open(django_settings.MEDIA_ROOT + f"{name}.pdf", 'rb')
                    # Return response
                    response = FileResponse(img)
                    return response
                else:
                    print("ERROR")
            except UnidentifiedImageError:
                return render(request, 'error.html')
            return render(request, 'index.html')